pespin has uploaded this change for review.

View Change

library/HTTP2_*: Add initial HTTP2 client/server support

An HTTP2 client is implemented through the HTTP2_Adapter module, while
an HTTP2 server is implemented throught the HTTP2_Server_Emulation
module.

This is just an initial working solution (already being used
clientand server side upcoming SMF_Tests 5G SBI patches), the interface
may be changes and extended in the future (eg. we may want to add some
intermediate layer using HTTP2_Message in the HTTP2_Adapter instead of
writing raw HTTP2_Send/HTTP2_Recv records.)

Change-Id: I3ab440517e925a7c402dd4055c223ef568405adc
---
M deps/Makefile
A library/HTTP2_Adapter.ttcn
A library/HTTP2_CodecPort.ttcn
A library/HTTP2_CodecPort_CtrlFunct.ttcn
A library/HTTP2_CodecPort_CtrlFunctDef.cc
A library/HTTP2_Functions.ttcn
A library/HTTP2_Server_Emulation.ttcn
A library/HTTP2_Templates.ttcn
8 files changed, 1,123 insertions(+), 0 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/49/43649/1
diff --git a/deps/Makefile b/deps/Makefile
index f230577..10c343c 100644
--- a/deps/Makefile
+++ b/deps/Makefile
@@ -16,6 +16,7 @@
titan.ProtocolModules.GRE \
titan.ProtocolModules.GTP_v13.5.0 \
titan.ProtocolModules.GTPv2_v13.7.0 \
+ titan.ProtocolModules.HTTP2 \
titan.ProtocolModules.ICMP \
titan.ProtocolModules.ICMPv6 \
titan.ProtocolModules.IP \
@@ -82,6 +83,7 @@
titan.ProtocolModules.GRE_commit= R.2.A-5-g654c446
titan.ProtocolModules.GTP_v13.5.0_commit= 6b769f985eb91bf5a4332f29faa4a043b23ce62e
titan.ProtocolModules.GTPv2_v13.7.0_commit= R.2.A-5-g85cb124
+titan.ProtocolModules.HTTP2_commit= 297ac60099b567c8819ad349e22f185ec89a9c58
titan.ProtocolModules.ICMP_commit= e49d9fb9f7de637b4bf4803dc6b6e911a8661640
titan.ProtocolModules.ICMPv6_commit= 46f4d9b6e1e3c794294a92588401a81e4881dd27
titan.ProtocolModules.IP_commit= 1be86705f39ae38f3c04b2109806ee20d25e91d0
diff --git a/library/HTTP2_Adapter.ttcn b/library/HTTP2_Adapter.ttcn
new file mode 100644
index 0000000..7892618
--- /dev/null
+++ b/library/HTTP2_Adapter.ttcn
@@ -0,0 +1,101 @@
+module HTTP2_Adapter {
+
+/* HTTP2 Adapter component
+ * (C) 2019 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2026 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All rights reserved.
+ *
+ * Released under the terms of GNU General Public License, Version 2 or
+ * (at your option) any later version.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+import from HTTP2_Types all;
+import from HTTP2_CodecPort all;
+import from HTTP2_CodecPort_CtrlFunct all;
+import from IPL4asp_Types all;
+import from IPL4asp_PortType all;
+import from Socket_API_Definitions all;
+
+import from HTTP2_Templates all;
+
+import from Native_Functions all;
+import from Misc_Helpers all;
+
+type component HTTP2_CT {
+ port HTTP2_CODEC_PT HTTP2;
+ /* double underscore to have "g_pars" available on components extending this one: */
+ var template (omit) HTTP2_Adapter_Params g_http2_pars;
+
+ /* Connection identifier of the client / server itself */
+ var IPL4asp_Types.ConnectionId g_self_conn_id := -1;
+};
+
+type record HTTP2_Adapter_Params {
+ charstring local_host,
+ integer local_port,
+ charstring remote_host,
+ integer remote_port,
+ boolean use_ssl
+};
+
+template (value) HTTP2_Adapter_Params
+ts_HTTP2_Adapter_Params(template (value) charstring local_host,
+ template (value) integer local_port,
+ template (value) charstring remote_host,
+ template (value) integer remote_port,
+ template (value) boolean use_ssl) := {
+ local_host := local_host,
+ local_port := local_port,
+ remote_host := remote_host,
+ remote_port := remote_port,
+ use_ssl := use_ssl
+}
+
+function f_http2_adapter_init() runs on HTTP2_CT {
+ map(self:HTTP2, system:HTTP2_CODEC_PT);
+}
+
+private function f_http2_adapter_getMsgLen(in octetstring pl_stream,
+ inout Socket_API_Definitions.ro_integer args)
+return integer {
+ var HTTP2_ro_integer args_unused := {};
+ return f_HTTP2_msglen(pl_stream, args_unused);
+}
+
+
+private function f_set_tcp_segmentation()
+runs on HTTP2_CT {
+ /* Set function for dissecting the binary stream into packets */
+ var f_IPL4_getMsgLen vl_f := refers(f_http2_adapter_getMsgLen);
+ HTTP2_CodecPort_CtrlFunct.f_IPL4_setGetMsgLen(HTTP2, g_self_conn_id, vl_f, {});
+}
+
+/* Function to use to connect as client to a remote IPA Server */
+function f_http2_adapter_connect(charstring remote_host, IPL4asp_Types.PortNumber remote_port,
+ charstring local_host, IPL4asp_Types.PortNumber local_port, boolean use_ssl := false)
+runs on HTTP2_CT {
+ var IPL4asp_Types.Result res;
+ res := HTTP2_CodecPort_CtrlFunct.f_IPL4_connect(HTTP2, remote_host, remote_port,
+ local_host, local_port, 0, { tcp:={} });
+ if (not ispresent(res.connId)) {
+ setverdict(fail, "Could not connect HTTP2 socket from ", local_host, " port ", local_port,
+ " to ", remote_host, " port ", remote_port, "; check your configuration");
+ mtc.stop;
+ }
+ g_self_conn_id := res.connId;
+ g_http2_pars := ts_HTTP2_Adapter_Params(remote_host, remote_port, local_host, local_port, use_ssl);
+
+ f_set_tcp_segmentation();
+ HTTP2.send(ts_HTTP2_Send_magic(g_self_conn_id));
+}
+
+function f_http2_adapter_send(template (value) HTTP2_Frame http2_frame)
+runs on HTTP2_CT
+{
+ HTTP2.send(ts_HTTP2_Send(g_self_conn_id, http2_frame));
+}
+
+}
diff --git a/library/HTTP2_CodecPort.ttcn b/library/HTTP2_CodecPort.ttcn
new file mode 100644
index 0000000..d36ff96
--- /dev/null
+++ b/library/HTTP2_CodecPort.ttcn
@@ -0,0 +1,108 @@
+module HTTP2_CodecPort {
+
+/* Simple HTTP2 Codec Port, translating between raw TCP octetstring payload
+ * towards the IPL4asp port provider, and HTTP2 primitives
+ * which carry the decoded HTTP2 data types as payload.
+ *
+ * (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
+ * All rights reserved.
+ *
+ * Released under the terms of GNU General Public License, Version 2 or
+ * (at your option) any later version.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+ import from IPL4asp_PortType all;
+ import from IPL4asp_Types all;
+ import from HTTP2_Types all;
+
+ type record HTTP2_Recv {
+ ConnectionId connId,
+ HTTP2_Frame frame optional,
+ HTTP2_decoder_error_descr pl_error_descr optional
+ }
+
+ template (present) HTTP2_Recv tr_HTTP2_Recv(template (present) ConnectionId connId := ?,
+ template HTTP2_Frame frame := *,
+ template HTTP2_decoder_error_descr pl_error_descr := *) := {
+ connId := connId,
+ frame := frame,
+ pl_error_descr := pl_error_descr
+ }
+
+ type record HTTP2_Send {
+ ConnectionId connId,
+ HTTP2_Frame frame
+ };
+
+ template (value) HTTP2_Send
+ ts_HTTP2_Send(template (value) ConnectionId connId,
+ template (value) HTTP2_Frame frame) := {
+ connId := connId,
+ frame := frame
+ }
+
+
+ type record HTTP2_Send_charstring {
+ ConnectionId connId,
+ charstring data
+ };
+
+ template (value) HTTP2_Send_charstring
+ ts_HTTP2_Send_charstring(template (value) ConnectionId connId,
+ template (value) charstring data) := {
+ connId := connId,
+ data := data
+ }
+
+ const charstring c_HTTP2_magic := "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
+
+ template (value) HTTP2_Send_charstring
+ ts_HTTP2_Send_magic(template (value) ConnectionId connId) :=
+ ts_HTTP2_Send_charstring(connId, c_HTTP2_magic);
+
+ private function IPL4_to_HTTP2_Recv(in ASP_RecvFrom pin, out HTTP2_Recv pout) {
+ var integer rc;
+ pout.connId := pin.connId;
+ if (lengthof(pin.msg) == lengthof(HTTP2_connection_preface) and
+ pin.msg == HTTP2_connection_preface) {
+ log(pin.connId, ": Rx HTTP2 preface");
+ pout.pl_error_descr := omit;
+ pout.frame := omit;
+ } else {
+ rc := f_HTTP2_decode_frame(pin.msg, pout.frame, pout.pl_error_descr);
+ if (rc == 0) {
+ pout.pl_error_descr := omit;
+ } else {
+ pout.frame := omit;
+ }
+ log(pin.connId, ": f_HTTP2_decode_frame(", pin.msg, ") returned ", rc);
+ }
+ } with { extension "prototype(fast)" }
+
+ private function HTTP2_to_IPL4_Send(in HTTP2_Send pin, out ASP_Send pout) {
+ pout.connId := pin.connId;
+ pout.proto := { tcp := {} };
+ pout.msg := f_HTTP2_encode_frame(pin.frame);
+ } with { extension "prototype(fast)" }
+
+ private function HTTP2_Send_charstring_to_IPL4_Send(in HTTP2_Send_charstring pin, out ASP_Send pout) {
+ pout.connId := pin.connId;
+ pout.proto := { tcp := {} };
+ pout.msg := char2oct(pin.data);
+ } with { extension "prototype(fast)" }
+
+ type port HTTP2_CODEC_PT message {
+ out HTTP2_Send, HTTP2_Send_charstring;
+ in HTTP2_Recv,
+ ASP_ConnId_ReadyToRelease,
+ ASP_Event;
+ } with { extension "user IPL4asp_PT
+ out(HTTP2_Send -> ASP_Send: function(HTTP2_to_IPL4_Send);
+ HTTP2_Send_charstring -> ASP_Send: function(HTTP2_Send_charstring_to_IPL4_Send))
+ in(ASP_RecvFrom -> HTTP2_Recv: function(IPL4_to_HTTP2_Recv);
+ ASP_ConnId_ReadyToRelease -> ASP_ConnId_ReadyToRelease: simple;
+ ASP_Event -> ASP_Event: simple)"
+ }
+}
diff --git a/library/HTTP2_CodecPort_CtrlFunct.ttcn b/library/HTTP2_CodecPort_CtrlFunct.ttcn
new file mode 100644
index 0000000..c4eb3a9
--- /dev/null
+++ b/library/HTTP2_CodecPort_CtrlFunct.ttcn
@@ -0,0 +1,52 @@
+module HTTP2_CodecPort_CtrlFunct {
+
+ import from HTTP2_CodecPort all;
+ import from IPL4asp_Types all;
+
+ external function f_IPL4_listen(
+ inout HTTP2_CODEC_PT portRef,
+ in HostName locName,
+ in PortNumber locPort,
+ in ProtoTuple proto,
+ in OptionList options := {}
+ ) return Result;
+
+ external function f_IPL4_connect(
+ inout HTTP2_CODEC_PT portRef,
+ in HostName remName,
+ in PortNumber remPort,
+ in HostName locName,
+ in PortNumber locPort,
+ in ConnectionId connId,
+ in ProtoTuple proto,
+ in OptionList options := {}
+ ) return Result;
+
+ external function f_IPL4_close(
+ inout HTTP2_CODEC_PT portRef,
+ in ConnectionId id,
+ in ProtoTuple proto := { unspecified := {} }
+ ) return Result;
+
+ external function f_IPL4_setUserData(
+ inout HTTP2_CODEC_PT portRef,
+ in ConnectionId id,
+ in UserData userData
+ ) return Result;
+
+ external function f_IPL4_getUserData(
+ inout HTTP2_CODEC_PT portRef,
+ in ConnectionId id,
+ out UserData userData
+ ) return Result;
+
+ external function f_IPL4_setGetMsgLen(
+ inout HTTP2_CODEC_PT portRef,
+ in ConnectionId id,
+ inout f_IPL4_getMsgLen f,
+ in ro_integer msgLenArgs
+ );
+
+
+}
+
diff --git a/library/HTTP2_CodecPort_CtrlFunctDef.cc b/library/HTTP2_CodecPort_CtrlFunctDef.cc
new file mode 100644
index 0000000..9d35fbd
--- /dev/null
+++ b/library/HTTP2_CodecPort_CtrlFunctDef.cc
@@ -0,0 +1,66 @@
+#include "IPL4asp_PortType.hh"
+#include "HTTP2_CodecPort.hh"
+#include "IPL4asp_PT.hh"
+
+namespace HTTP2__CodecPort__CtrlFunct {
+
+ IPL4asp__Types::Result f__IPL4__listen(
+ HTTP2__CodecPort::HTTP2__CODEC__PT& portRef,
+ const IPL4asp__Types::HostName& locName,
+ const IPL4asp__Types::PortNumber& locPort,
+ const IPL4asp__Types::ProtoTuple& proto,
+ const IPL4asp__Types::OptionList& options)
+ {
+ return f__IPL4__PROVIDER__listen(portRef, locName, locPort, proto, options);
+ }
+
+ IPL4asp__Types::Result f__IPL4__connect(
+ HTTP2__CodecPort::HTTP2__CODEC__PT& portRef,
+ const IPL4asp__Types::HostName& remName,
+ const IPL4asp__Types::PortNumber& remPort,
+ const IPL4asp__Types::HostName& locName,
+ const IPL4asp__Types::PortNumber& locPort,
+ const IPL4asp__Types::ConnectionId& connId,
+ const IPL4asp__Types::ProtoTuple& proto,
+ const IPL4asp__Types::OptionList& options)
+ {
+ return f__IPL4__PROVIDER__connect(portRef, remName, remPort,
+ locName, locPort, connId, proto, options);
+ }
+
+ IPL4asp__Types::Result f__IPL4__close(
+ HTTP2__CodecPort::HTTP2__CODEC__PT& portRef,
+ const IPL4asp__Types::ConnectionId& connId,
+ const IPL4asp__Types::ProtoTuple& proto)
+ {
+ return f__IPL4__PROVIDER__close(portRef, connId, proto);
+ }
+
+ IPL4asp__Types::Result f__IPL4__setUserData(
+ HTTP2__CodecPort::HTTP2__CODEC__PT& portRef,
+ const IPL4asp__Types::ConnectionId& connId,
+ const IPL4asp__Types::UserData& userData)
+ {
+ return f__IPL4__PROVIDER__setUserData(portRef, connId, userData);
+ }
+
+ IPL4asp__Types::Result f__IPL4__getUserData(
+ HTTP2__CodecPort::HTTP2__CODEC__PT& portRef,
+ const IPL4asp__Types::ConnectionId& connId,
+ IPL4asp__Types::UserData& userData)
+ {
+ return f__IPL4__PROVIDER__getUserData(portRef, connId, userData);
+ }
+
+ void f__IPL4__setGetMsgLen(
+ HTTP2__CodecPort::HTTP2__CODEC__PT& portRef,
+ const IPL4asp__Types::ConnectionId& connId,
+ Socket__API__Definitions::f__getMsgLen& f,
+ const Socket__API__Definitions::ro__integer& msgLenArgs)
+ {
+ return f__IPL4__PROVIDER__setGetMsgLen(portRef, connId, f, msgLenArgs);
+ }
+
+
+}
+
diff --git a/library/HTTP2_Functions.ttcn b/library/HTTP2_Functions.ttcn
new file mode 100644
index 0000000..95e5ea1
--- /dev/null
+++ b/library/HTTP2_Functions.ttcn
@@ -0,0 +1,32 @@
+module HTTP2_Functions {
+
+import from HTTP2_Types all;
+import from HTTP2_Templates all;
+
+import from TCCConversion_Functions all; //f_strstr()
+
+function f_HTTP2_header_list_find_header(HTTP2_header_list headers, charstring header_name)
+return template (omit) charstring {
+ for (var integer i := 0; i < lengthof(headers); i := i + 1) {
+ if (headers[i].header_name == header_name) {
+ return headers[i].header_value;
+ }
+ }
+ return omit;
+}
+
+function f_HTTP2_header_content_type_multipart_val_get_boundary(charstring content_type_val)
+return charstring {
+ var charstring boundary;
+ var integer pos := f_strstr(content_type_val, "boundary=\"", 0);
+ if (pos < 0) {
+ return "";
+ }
+ pos := pos + lengthof("boundary=\"");
+ // -1: Remove trailing "\""
+ boundary := substr(content_type_val, pos, lengthof(content_type_val) - pos - 1);
+ return boundary;
+}
+
+
+}
diff --git a/library/HTTP2_Server_Emulation.ttcn b/library/HTTP2_Server_Emulation.ttcn
new file mode 100644
index 0000000..779d7f3
--- /dev/null
+++ b/library/HTTP2_Server_Emulation.ttcn
@@ -0,0 +1,526 @@
+/* HTTP2 Emulation in TTCN-3
+ *
+ * (C) 2026 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Released under the terms of GNU General Public License, Version 2 or
+ * (at your option) any later version.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This module implements a HTTP/HTTPs 2.0 server,
+ */
+
+module HTTP2_Server_Emulation {
+
+import from HTTP2_Types all;
+import from HTTP2_CodecPort all;
+import from HTTP2_CodecPort_CtrlFunct all;
+import from IPL4asp_Types all;
+import from IPL4asp_PortType all;
+import from Socket_API_Definitions all;
+
+import from HTTP2_Templates all;
+
+import from Native_Functions all;
+import from Misc_Helpers all;
+
+
+/***************
+ * HTTP2_Msg
+ **************/
+
+type record HTTP2_Msg {
+ IPL4asp_Types.ConnectionId conn_id,
+ integer stream_id,
+ HTTP2_header_block hb,
+ HTTP2_Data_frame body optional
+}
+
+template (present) HTTP2_Msg
+tr_HTTP2_Msg(template (present) IPL4asp_Types.ConnectionId conn_id := ?,
+ template (present) integer stream_id := ?,
+ template (present) HTTP2_header_block hb := ?,
+ template HTTP2_Data_frame body := *) := {
+ conn_id := conn_id,
+ stream_id := stream_id,
+ hb := hb,
+ body := body
+}
+
+template (value) HTTP2_Msg
+ts_HTTP2_Msg(template (value) IPL4asp_Types.ConnectionId conn_id,
+ template (value) integer stream_id,
+ template (value) HTTP2_header_block hb,
+ template (omit) HTTP2_Data_frame body := omit) := {
+ conn_id := conn_id,
+ stream_id := stream_id,
+ hb := hb,
+ body := body
+}
+
+template (value) HTTP2_Msg
+ts_HTTP2_Msg_Status(template (value) IPL4asp_Types.ConnectionId conn_id,
+ template (value) integer stream_id,
+ template (value) integer status,
+ template (omit) HTTP2_header_list headers := omit,
+ template (omit) HTTP2_Data_frame body := omit) :=
+ts_HTTP2_Msg(conn_id,
+ stream_id,
+ hb := ts_HTTP2_header_block(pseudo_headers := ts_HTTP2_pseudo_headers(status := status),
+ headers := headers),
+ body := body
+ );
+
+template (value) HTTP2_Msg
+ts_HTTP2_Msg_200_OK(template (value) IPL4asp_Types.ConnectionId conn_id,
+ template (value) integer stream_id,
+ template (omit) HTTP2_header_list headers := omit,
+ template (omit) HTTP2_Data_frame body := omit) :=
+ts_HTTP2_Msg_Status(conn_id, stream_id, 200, headers, body);
+
+template (value) HTTP2_Msg
+ts_HTTP2_Msg_201_Created(template (value) IPL4asp_Types.ConnectionId conn_id,
+ template (value) integer stream_id,
+ template (omit) HTTP2_header_list headers := omit,
+ template (omit) HTTP2_Data_frame body := omit) :=
+ts_HTTP2_Msg_Status(conn_id, stream_id, 201, headers, body);
+
+
+function f_HTTP2_Msg_append_hdr_content_length(inout HTTP2_Msg http2_msg)
+{
+ if (not ispresent(http2_msg.body)) {
+ return;
+ }
+ var integer content_length := lengthof(http2_msg.body.data); /* TODO: http2_msg.body.padding ? */
+ http2_msg.hb.headers := http2_msg.hb.headers &
+ { valueof(ts_HTTP2_Hdr_content_length(int2str(content_length))) };
+}
+
+/*********************
+ * HTTP2_Server_Conn
+ ********************/
+type enumerated HTTP2_Server_Conn_State {
+ HTTP2_SERVER_CONN_ST_UNUSED,
+ HTTP2_SERVER_CONN_ST_WAIT_PREFACE,
+ HTTP2_SERVER_CONN_ST_WAIT_SETTINGS,
+ HTTP2_SERVER_CONN_ST_WAIT_HEADER,
+ HTTP2_SERVER_CONN_ST_WAIT_BODY
+}
+
+/* An HTTP2 connection established against the HTTP2 Server */
+type record HTTP2_Server_Conn {
+ IPL4asp_Types.ConnectionId conn_id,
+ HTTP2_Server_Conn_State state,
+ HTTP2_header_block rx_hb optional /* Contains received header while in HTTP2_SERVER_CONN_ST_WAIT_BODY */
+}
+
+private function f_HTTP2_Server_Conn_init(inout HTTP2_Server_Conn conn)
+{
+ conn := {
+ conn_id := -1,
+ state := HTTP2_SERVER_CONN_ST_UNUSED,
+ rx_hb := omit
+ };
+}
+
+private function f_HTTP2_Server_Conn_str(in HTTP2_Server_Conn conn) return charstring {
+ return "HTTP2_Server_Conn(" & int2str(conn.conn_id) & "," & log2str(conn.state) & ")";
+}
+
+private function f_HTTP2_Server_Conn_st_change(inout HTTP2_Server_Conn conn, HTTP2_Server_Conn_State state)
+{
+ log(f_HTTP2_Server_Conn_str(conn), ": state change to ", state);
+ conn.state := state
+}
+
+private function f_HTTP2_Server_Conn_comp_decode(inout HTTP2_Server_Conn conn, octetstring header_block_fragment)
+return HTTP2_header_block
+{
+ var HTTP2_header_block pl_hblock;
+ var HTTP2_comp_context ctx;
+ var integer rc;
+
+ ctx := HTTP2_comp_context_init();
+ rc := HTTP2_comp_context_decode(ctx, pl_hblock, header_block_fragment);
+ HTTP2_comp_context_free(ctx);
+ if (rc != 0) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str(f_HTTP2_Server_Conn_str(conn),
+ ": HTTP2_comp_context_decode returned ", rc));
+ }
+
+ return pl_hblock;
+}
+
+private function f_HTTP2_Server_Conn_comp_encode(inout HTTP2_Server_Conn conn, HTTP2_header_block hb)
+return octetstring
+{
+ var octetstring pl_frame_data;
+ var HTTP2_comp_context ctx;
+ var integer rc;
+
+ ctx := HTTP2_comp_context_init();
+ rc := HTTP2_comp_context_encode(ctx, hb, pl_frame_data);
+ HTTP2_comp_context_free(ctx);
+ if (rc != 0) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str(f_HTTP2_Server_Conn_str(conn),
+ ": HTTP2_comp_context_encode returned ", rc));
+ }
+
+ return pl_frame_data;
+}
+
+/***********************************************************************
+ * Main Emulation Component (HTTP2 Server)
+ ***********************************************************************/
+
+type record Http2ServerEmulationCfg {
+ charstring http2_bind_ip,
+ integer http2_bind_port
+};
+
+type component HTTP2_Server_Emulation_CT {
+ /* Communication with underlying HTTP2 CodecPort */
+ port HTTP2_CODEC_PT HTTP2;
+
+ /* Communication with Clients */
+ port HTTP2_CONN_PT CONN;
+ port HTTP2_CONN_PROC_PT CONN_PROC;
+
+ /* Configuration by the user */
+ var Http2ServerEmulationCfg g_http2_cfg;
+
+ /* Connection identifier of the server itself */
+ var IPL4asp_Types.ConnectionId g_self_conn_id := -1;
+ var HTTP2_Server_Conn conn_table[16];
+ var HTTP2_ConnHdlr vc_ConnHdlr_table[16];
+};
+
+private function f_conn_table_init()
+runs on HTTP2_Server_Emulation_CT {
+ var integer i;
+ for (i := 0; i < sizeof(conn_table); i := i + 1) {
+ f_HTTP2_Server_Conn_init(conn_table[i]);
+ }
+}
+
+private function f_conn_table_find_by_conn_id(IPL4asp_Types.ConnectionId conn_id)
+runs on HTTP2_Server_Emulation_CT return integer {
+ var integer i;
+ for (i := 0; i < sizeof(conn_table); i := i + 1) {
+ if (conn_table[i].conn_id == conn_id) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+private function f_conn_table_find_free()
+runs on HTTP2_Server_Emulation_CT return integer {
+ return f_conn_table_find_by_conn_id(-1);
+}
+
+private function f_vc_ConnHdlr_table_add(HTTP2_ConnHdlr vc_conn)
+runs on HTTP2_Server_Emulation_CT {
+ var integer i;
+ for (i := 0; i < sizeof(vc_ConnHdlr_table); i := i + 1) {
+ if (not isbound(vc_ConnHdlr_table[i])) {
+ vc_ConnHdlr_table[i] := vc_conn;
+ return;
+ }
+ }
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str("No Space in vc_ConnHdlr_table[i] for ", vc_conn));
+}
+
+private function f_http2_adapter_getMsgLen(in octetstring pl_stream,
+ inout Socket_API_Definitions.ro_integer args)
+return integer {
+ var HTTP2_ro_integer args_unused := {};
+ return f_HTTP2_msglen(pl_stream, args_unused);
+}
+
+
+private function f_set_tcp_segmentation()
+runs on HTTP2_Server_Emulation_CT {
+ /* Set function for dissecting the binary stream into packets */
+ var f_IPL4_getMsgLen vl_f := refers(f_http2_adapter_getMsgLen);
+ HTTP2_CodecPort_CtrlFunct.f_IPL4_setGetMsgLen(HTTP2, g_self_conn_id, vl_f, {});
+}
+
+/* Function to use to connect as client to a remote HTTP2 Server */
+function f_bind(charstring local_host, IPL4asp_Types.PortNumber local_port)
+runs on HTTP2_Server_Emulation_CT {
+ var IPL4asp_Types.Result res;
+ res := HTTP2_CodecPort_CtrlFunct.f_IPL4_listen(HTTP2, local_host, local_port, { tcp:={} });
+ if (not ispresent(res.connId)) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str("Could not listen HTTP2 socket ",
+ local_host, ":", local_port,
+ ", check your configuration"));
+ }
+ g_self_conn_id := res.connId;
+}
+
+private function f_init(Http2ServerEmulationCfg cfg)
+runs on HTTP2_Server_Emulation_CT {
+
+ g_http2_cfg := cfg;
+
+ f_conn_table_init();
+
+ /* Start HTTP2 server */
+ map(self:HTTP2, system:HTTP2_CODEC_PT);
+ f_bind(g_http2_cfg.http2_bind_ip, g_http2_cfg.http2_bind_port);
+
+ f_set_tcp_segmentation();
+}
+
+/* Forward request to all registered clients */
+private function forward_http2_msg(HTTP2_Msg http2_msg)
+runs on HTTP2_Server_Emulation_CT {
+
+ for (var integer i := 0; i < sizeof(vc_ConnHdlr_table); i := i + 1) {
+ if (isbound(vc_ConnHdlr_table[i])) {
+ CONN.send(http2_msg) to vc_ConnHdlr_table[i];
+ }
+ }
+}
+
+private function f_HTTP2_Server_Conn_tx(inout HTTP2_Server_Conn conn, template (value) HTTP2_Frame http2_frame)
+runs on HTTP2_Server_Emulation_CT {
+ var HTTP2_Send http2_tx;
+ http2_tx := valueof(ts_HTTP2_Send(conn.conn_id, valueof(http2_frame)));
+ HTTP2.send(http2_tx);
+}
+
+private function f_HTTP2_Server_Conn_rx_http2(inout HTTP2_Server_Conn conn, HTTP2_Recv http2_rx)
+runs on HTTP2_Server_Emulation_CT {
+ var template (value) HTTP2_Msg http2_msg;
+ var template (value) HTTP2_Frame http2_frame;
+
+ if (ispresent(http2_rx.pl_error_descr)) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str(f_HTTP2_Server_Conn_str(conn), ": Rx HTTP2 decode error: ", http2_rx));
+ }
+
+ select (conn.state) {
+ case (HTTP2_SERVER_CONN_ST_UNUSED) { // created
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str(f_HTTP2_Server_Conn_str(conn), ": Rx in unexpected state: ", http2_rx));
+ }
+ case (HTTP2_SERVER_CONN_ST_WAIT_PREFACE) {
+ /* We expect to receive an empty frame, signalled by HTTP2_Codec_port meaning the magic/preface was received. */
+ if (ispresent(http2_rx.frame) or ispresent(http2_rx.pl_error_descr)) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str(conn, ": Rx in unexpected state: ", http2_rx));
+ }
+ var template (value) HTTP2_Setting_list settings := {
+ ts_HTTP2_Setting_MAX_FRAME_SIZE(1048576),
+ ts_HTTP2_Setting_MAX_CONCURRENT_STREAMS(250),
+ ts_HTTP2_Setting_MAX_HEADER_LIST_SIZE(10485760),
+ ts_HTTP2_Setting_INITIAL_WINDOW_SIZE(1048576)
+ };
+ http2_frame := ts_HTTP2_Frame_Settings(settings := settings);
+ f_HTTP2_Server_Conn_tx(conn, http2_frame);
+ f_HTTP2_Server_Conn_st_change(conn, HTTP2_SERVER_CONN_ST_WAIT_SETTINGS);
+ return;
+ }
+ case (HTTP2_SERVER_CONN_ST_WAIT_SETTINGS) {
+ if (not ispresent(http2_rx.frame)) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str(f_HTTP2_Server_Conn_str(conn), ": Rx unexpected: ", http2_rx));
+ }
+ if (not match(http2_rx.frame, tr_HTTP2_Frame_Settings())) {
+ return; /* TODO: we should only allow WINDOW_UPDATE here? */
+ }
+ /* Tx Settings ACK and Window Update: */
+ f_HTTP2_Server_Conn_tx(conn, ts_HTTP2_Frame_Settings(ack_flag := true));
+ f_HTTP2_Server_Conn_tx(conn, ts_HTTP2_Frame_Window_Update(0, 1073741824));
+ f_HTTP2_Server_Conn_st_change(conn, HTTP2_SERVER_CONN_ST_WAIT_HEADER);
+ return;
+ }
+ case (HTTP2_SERVER_CONN_ST_WAIT_HEADER) {
+ if (not ispresent(http2_rx.frame)) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str(f_HTTP2_Server_Conn_str(conn), ": Rx unexpected: ", http2_rx));
+ }
+ if (not match(http2_rx.frame, tr_HTTP2_Frame_Header())) {
+
+ return; /* TODO: we should only allow WINDOW_UPDATE and SETTINGS here... */
+ }
+
+ var HTTP2_header_block hb;
+ hb := f_HTTP2_Server_Conn_comp_decode(conn, http2_rx.frame.header_frame.header_block_fragment);
+ /* TODO: store header in Conn or send to ConNHdlr? */
+ log(f_HTTP2_Server_Conn_str(conn), ": Decompressed header: ", hb);
+ if (not http2_rx.frame.header_frame.end_stream_flag) {
+ conn.rx_hb := hb;
+ f_HTTP2_Server_Conn_st_change(conn, HTTP2_SERVER_CONN_ST_WAIT_BODY);
+ return;
+ }
+
+ http2_msg := ts_HTTP2_Msg(conn.conn_id, http2_rx.frame.header_frame.stream_id, hb, omit);
+ forward_http2_msg(valueof(http2_msg));
+ return;
+ }
+ case (HTTP2_SERVER_CONN_ST_WAIT_BODY) {
+ if (not ispresent(http2_rx.frame)) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str(f_HTTP2_Server_Conn_str(conn), ": Rx unexpected: ", http2_rx));
+ }
+ if (not match(http2_rx.frame, tr_HTTP2_Frame_Data())) {
+ return; /* TODO: we should only allow WINDOW_UPDATE and SETTINGS here... */
+ }
+ if (not http2_rx.frame.data_frame.end_stream_flag) {
+ log("FIXME: Multiple DATA blocks not supported! requires updating HTTP2_Msg.body to be a list!");
+ setverdict(fail);
+ }
+ http2_msg := ts_HTTP2_Msg(conn.conn_id,
+ http2_rx.frame.data_frame.stream_id,
+ conn.rx_hb,
+ http2_rx.frame.data_frame);
+ forward_http2_msg(valueof(http2_msg));
+ return;
+ }
+ case else {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str(f_HTTP2_Server_Conn_str(conn), ": Rx in unexpected state: ", http2_rx));
+ }
+ }
+}
+
+function main(Http2ServerEmulationCfg cfg)
+runs on HTTP2_Server_Emulation_CT {
+
+ var HTTP2_ConnHdlr vc_conn;
+ var ASP_Event asp_evt;
+ var HTTP2_Recv http2_rx;
+ var HTTP2_Send http2_tx;
+ var HTTP2_Msg http2_msg;
+ var template (value) HTTP2_Frame http2_frame;
+ var integer idx;
+
+ f_init(cfg);
+
+ while(true) {
+ alt {
+
+ /* server only */
+ [] HTTP2.receive(ASP_Event:{connOpened:=?}) -> value asp_evt {
+ //log("HTTP2_Server: Rx ", asp_evt);
+ log("HTTP2_Server: Established a new connection (conn_id=", asp_evt.connOpened.connId, ")");
+ idx := f_conn_table_find_by_conn_id(asp_evt.connOpened.connId);
+ if (idx >= 0) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str("Rx connOpened for already existing conn_id ",
+ asp_evt.connOpened.connId));
+ }
+ idx := f_conn_table_find_free();
+ if (idx < 0) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "No more space in conn_table!");
+ }
+ conn_table[idx].conn_id := asp_evt.connOpened.connId;
+ f_HTTP2_Server_Conn_st_change(conn_table[idx], HTTP2_SERVER_CONN_ST_WAIT_PREFACE);
+ }
+
+ [] HTTP2.receive(ASP_Event:{connClosed:=?}) -> value asp_evt {
+ //log("HTTP2_Server: Rx ", asp_evt);
+ idx := f_conn_table_find_by_conn_id(asp_evt.connClosed.connId);
+ if (idx < 0) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str("Rx connClosed for unknown conn_id ",
+ asp_evt.connClosed.connId));
+ }
+ // TODO: Notify/Remove ConnHdlrs?
+ f_HTTP2_Server_Conn_init(conn_table[idx]);
+ }
+
+ [] HTTP2.receive(HTTP2_Recv:?) -> value http2_rx {
+ //log("HTTP2_Server: Rx ", http2_rx);
+ idx := f_conn_table_find_by_conn_id(http2_rx.connId);
+ if (idx < 0) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str("Rx HTTP2_Recv ", http2_rx, " for unknown conn_id ",
+ asp_evt.connClosed.connId));
+ }
+ f_HTTP2_Server_Conn_rx_http2(conn_table[idx], http2_rx);
+ }
+
+ [] CONN.receive(HTTP2_Msg:?) -> value http2_msg sender vc_conn {
+ //log("HTTP2_Server: Tx ", http2_msg);
+ var integer last_stream_id := http2_msg.stream_id;
+
+ idx := f_conn_table_find_by_conn_id(http2_msg.conn_id);
+ if (idx < 0) {
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+ log2str("Rx HTTP2_Msg ", http2_rx, " for unknown conn_id ",
+ http2_msg.conn_id));
+ }
+
+ /* Send Header: */
+ f_HTTP2_Msg_append_hdr_content_length(http2_msg);
+ var octetstring hdr_enc := f_HTTP2_Server_Conn_comp_encode(conn_table[idx], http2_msg.hb);
+ http2_frame := ts_HTTP2_Frame_Header(http2_msg.stream_id,
+ end_stream_flag := not ispresent(http2_msg.body),
+ end_header_flag := true,
+ header_block_fragment := hdr_enc);
+ f_HTTP2_Server_Conn_tx(conn_table[idx], http2_frame);
+
+ /* Send body: */
+ if (ispresent(http2_msg.body)) {
+ http2_frame := ts_HTTP2_Frame_Data(http2_msg.body.stream_id,
+ http2_msg.body.end_stream_flag,
+ http2_msg.body.data,
+ http2_msg.body.padding);
+ f_HTTP2_Server_Conn_tx(conn_table[idx], http2_frame);
+ }
+ /* Send GOAWAY since we are finished: */
+ f_HTTP2_Server_Conn_tx(conn_table[idx], ts_HTTP2_Frame_Goaway(last_stream_id, 0 /* NO_ERROR */));
+ }
+ [] CONN_PROC.getcall(HTTP2EM_register:{}) -> sender vc_conn {
+ //log("HTTP2_Server: Register ", vc_conn);
+ f_vc_ConnHdlr_table_add(vc_conn);
+ CONN_PROC.reply(HTTP2EM_register:{}) to vc_conn;
+ }
+ }
+ }
+
+}
+
+
+/***********************************************************************
+ * Interaction between Main and Client Components
+ ***********************************************************************/
+
+type port HTTP2_CONN_PT message {
+ inout HTTP2_Msg;
+} with { extension "internal" };
+
+signature HTTP2EM_register();
+
+type port HTTP2_CONN_PROC_PT procedure {
+ inout HTTP2EM_register;
+} with { extension "internal" };
+
+
+/***********************************************************************
+ * Client Component
+ ***********************************************************************/
+
+type component HTTP2_ConnHdlr {
+ /* These ports are not connected to anything in this component, they
+ * serve as connection points for the component that extends this
+ * coponent. */
+ port HTTP2_CONN_PT HTTP2_CONN[16];
+ port HTTP2_CONN_PROC_PT HTTP2_CONN_PROC[16];
+};
+
+function f_http2_server_emu_register(integer id := 0) runs on HTTP2_ConnHdlr {
+ HTTP2_CONN_PROC[id].call(HTTP2EM_register:{}) {
+ [] HTTP2_CONN_PROC[id].getreply(HTTP2EM_register:{});
+ }
+}
+
+}
diff --git a/library/HTTP2_Templates.ttcn b/library/HTTP2_Templates.ttcn
new file mode 100644
index 0000000..d6d8296
--- /dev/null
+++ b/library/HTTP2_Templates.ttcn
@@ -0,0 +1,236 @@
+module HTTP2_Templates {
+
+import from HTTP2_Types all;
+
+template (value) HTTP2_Setting_data
+ts_HTTP2_Setting_data(template (value) integer setting_id,
+ template (value) integer setting_value) := {
+ setting_id := setting_id,
+ setting_value := setting_value
+}
+
+template (value) HTTP2_Setting_data
+ts_HTTP2_Setting_HEADER_TABLE_SIZE(template (value) integer setting_value) :=
+ts_HTTP2_Setting_data(c_SETTINGS_HEADER_TABLE_SIZE, setting_value);
+
+template (value) HTTP2_Setting_data
+ts_HTTP2_Setting_ENABLE_PUSH(template (value) integer setting_value) :=
+ts_HTTP2_Setting_data(c_SETTINGS_ENABLE_PUSH, setting_value);
+
+template (value) HTTP2_Setting_data
+ts_HTTP2_Setting_MAX_CONCURRENT_STREAMS(template (value) integer setting_value) :=
+ts_HTTP2_Setting_data(c_SETTINGS_MAX_CONCURRENT_STREAMS, setting_value);
+
+template (value) HTTP2_Setting_data
+ts_HTTP2_Setting_INITIAL_WINDOW_SIZE(template (value) integer setting_value) :=
+ts_HTTP2_Setting_data(c_SETTINGS_INITIAL_WINDOW_SIZE, setting_value);
+
+template (value) HTTP2_Setting_data
+ts_HTTP2_Setting_MAX_FRAME_SIZE(template (value) integer setting_value) :=
+ts_HTTP2_Setting_data(c_SETTINGS_MAX_FRAME_SIZE, setting_value);
+
+template (value) HTTP2_Setting_data
+ts_HTTP2_Setting_MAX_HEADER_LIST_SIZE(template (value) integer setting_value) :=
+ts_HTTP2_Setting_data(c_SETTINGS_MAX_HEADER_LIST_SIZE, setting_value);
+
+template (present) HTTP2_Data_frame
+tr_HTTP2_Data_frame(template (present) integer stream_id := ?,
+ template (present) boolean end_stream_flag := ?,
+ template (present) octetstring data := ?,
+ template octetstring padding := *) := {
+ stream_id := stream_id,
+ end_stream_flag := end_stream_flag,
+ data := data,
+ padding := padding
+}
+
+template (value) HTTP2_Data_frame
+ts_HTTP2_Data_frame(template (value) integer stream_id := 0,
+ template (value) boolean end_stream_flag := false,
+ template (value) octetstring data,
+ template (omit) octetstring padding := omit) := {
+ stream_id := stream_id,
+ end_stream_flag := end_stream_flag,
+ data := data,
+ padding := padding
+}
+
+
+template (present) HTTP2_Frame
+tr_HTTP2_Frame_Settings(template (present) boolean ack_flag := ?,
+ template HTTP2_Setting_list settings := *) := {
+ settings_frame := {
+ ack_flag := ack_flag,
+ settings := settings
+ }
+}
+
+template (value) HTTP2_Frame
+ts_HTTP2_Frame_Settings(template (value) boolean ack_flag := false,
+ template (omit) HTTP2_Setting_list settings := omit) := {
+ settings_frame := {
+ ack_flag := ack_flag,
+ settings := settings
+ }
+}
+
+template (value) HTTP2_Frame
+ts_HTTP2_Frame_Goaway(template (value) integer last_stream_id,
+ template (value) integer error_code,
+ template (omit) octetstring debug_data := omit) := {
+ goaway_frame := {
+ last_stream_id := last_stream_id,
+ error_code := error_code,
+ debug_data := debug_data
+ }
+}
+
+template (value) HTTP2_Frame
+ts_HTTP2_Frame_Window_Update(template (value) integer stream_id := 0,
+ template (value) integer window_size_increment) := {
+ window_update_frame := {
+ stream_id := stream_id,
+ window_size_increment := window_size_increment
+ }
+}
+
+template (present) HTTP2_Frame
+tr_HTTP2_Frame_Data(template (present) integer stream_id := ?,
+ template (present) boolean end_stream_flag := ?,
+ template (present) octetstring data := ?,
+ template octetstring padding := *) := {
+ data_frame := tr_HTTP2_Data_frame(stream_id,
+ end_stream_flag,
+ data,
+ padding)
+}
+
+template (value) HTTP2_Frame
+ts_HTTP2_Frame_Data(template (value) integer stream_id := 0,
+ template (value) boolean end_stream_flag := false,
+ template (value) octetstring data,
+ template (omit) octetstring padding := omit) := {
+ data_frame := ts_HTTP2_Data_frame(stream_id,
+ end_stream_flag,
+ data,
+ padding)
+}
+
+template (present) HTTP2_Frame
+tr_HTTP2_Frame_Header(template (present) integer stream_id := ?,
+ template (present) boolean end_stream_flag := ?,
+ template (present) boolean end_header_flag := ?,
+ template HTTP2_Priority_data priority_data := *,
+ template (present) octetstring header_block_fragment := ?,
+ template octetstring padding := *) := {
+ header_frame := {
+ stream_id := stream_id,
+ end_stream_flag := end_stream_flag,
+ end_header_flag := end_header_flag,
+ priority_data := priority_data,
+ header_block_fragment := header_block_fragment,
+ padding := padding
+ }
+}
+
+template (value) HTTP2_Frame
+ts_HTTP2_Frame_Header(template (value) integer stream_id := 0,
+ template (value) boolean end_stream_flag := false,
+ template (value) boolean end_header_flag := false,
+ template (omit) HTTP2_Priority_data priority_data := omit,
+ template (value) octetstring header_block_fragment := ''O,
+ template (omit) octetstring padding := omit) := {
+ header_frame := {
+ stream_id := stream_id,
+ end_stream_flag := end_stream_flag,
+ end_header_flag := end_header_flag,
+ priority_data := priority_data,
+ header_block_fragment := header_block_fragment,
+ padding := padding
+ }
+}
+
+template (present) HTTP2_pseudo_headers
+tr_HTTP2_pseudo_headers(template charstring method := *,
+ template charstring scheme := *,
+ template charstring authority := *,
+ template charstring path := *,
+ template integer status := *) := {
+ method := method,
+ scheme := scheme,
+ authority := authority,
+ path := path,
+ status := status
+}
+
+template (value) HTTP2_pseudo_headers
+ts_HTTP2_pseudo_headers(template (omit) charstring method := omit,
+ template (omit) charstring scheme := omit,
+ template (omit) charstring authority := omit,
+ template (omit) charstring path := omit,
+ template (omit) integer status := omit) := {
+ method := method,
+ scheme := scheme,
+ authority := authority,
+ path := path,
+ status := status
+}
+
+template (value) HTTP2_header_field
+ts_HTTP2_header_field(template (value) charstring header_name,
+ template (omit) charstring header_value := omit) := {
+ header_name := header_name,
+ header_value := header_value
+}
+
+const charstring c_HTTP2_HDR_content_type := "content-type";
+const charstring c_HTTP2_HDR_accept := "accept";
+const charstring c_HTTP2_HDR_user_agent := "user-agent";
+const charstring c_HTTP2_HDR_content_length := "content-length";
+const charstring c_HTTP2_HDR_accept_encoding := "accept-encoding";
+const charstring c_HTTP2_HDR_status := "status";
+const charstring c_HTTP2_HDR_location := "location";
+
+template (value) HTTP2_header_field
+ts_HTTP2_Hdr_content_type(template (omit) charstring header_value := omit)
+:= ts_HTTP2_header_field(c_HTTP2_HDR_content_type, header_value);
+
+template (value) HTTP2_header_field
+ts_HTTP2_Hdr_accept(template (omit) charstring header_value := omit)
+:= ts_HTTP2_header_field(c_HTTP2_HDR_accept, header_value);
+
+template (value) HTTP2_header_field
+ts_HTTP2_Hdr_user_agent(template (omit) charstring header_value := omit)
+:= ts_HTTP2_header_field(c_HTTP2_HDR_user_agent, header_value);
+
+template (value) HTTP2_header_field
+ts_HTTP2_Hdr_content_length(template (omit) charstring header_value := omit)
+:= ts_HTTP2_header_field(c_HTTP2_HDR_content_length, header_value);
+
+template (value) HTTP2_header_field
+ts_HTTP2_Hdr_accept_encoding(template (omit) charstring header_value := omit)
+:= ts_HTTP2_header_field(c_HTTP2_HDR_accept_encoding, header_value);
+
+template (value) HTTP2_header_field
+ts_HTTP2_Hdr_status(template (omit) charstring header_value := omit)
+:= ts_HTTP2_header_field(c_HTTP2_HDR_status, header_value);
+
+template (value) HTTP2_header_field
+ts_HTTP2_Hdr_location(template (omit) charstring header_value := omit)
+:= ts_HTTP2_header_field(c_HTTP2_HDR_location, header_value);
+
+
+template (present) HTTP2_header_block
+tr_HTTP2_header_block(template HTTP2_pseudo_headers pseudo_headers := *,
+ template HTTP2_header_list headers := *) := {
+ pseudo_headers := pseudo_headers,
+ headers := headers
+}
+template (value) HTTP2_header_block
+ts_HTTP2_header_block(template (omit) HTTP2_pseudo_headers pseudo_headers := omit,
+ template (omit) HTTP2_header_list headers := omit) := {
+ pseudo_headers := pseudo_headers,
+ headers := headers
+}
+
+}

To view, visit change 43649. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Change-Id: I3ab440517e925a7c402dd4055c223ef568405adc
Gerrit-Change-Number: 43649
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin@sysmocom.de>