diff options
Diffstat (limited to 'library/HTTP_Adapter.ttcn')
-rw-r--r-- | library/HTTP_Adapter.ttcn | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/library/HTTP_Adapter.ttcn b/library/HTTP_Adapter.ttcn new file mode 100644 index 00000000..d970416e --- /dev/null +++ b/library/HTTP_Adapter.ttcn @@ -0,0 +1,281 @@ +module HTTP_Adapter { + +/* HTTP Adapter component, originally part of Integration Tests for osmo-remsim-server + * (C) 2019 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 + * + * This test suite tests osmo-remsim-server by attaching to the external interfaces + * such as RSPRO for simulated clients + bankds and RSRES (REST backend interface). + */ + +import from HTTPmsg_Types all; +import from HTTPmsg_PortType all; +import from Native_Functions all; + +type component http_CT { + port HTTPmsg_PT HTTP; + /* double underscore to have "g_pars" available on components extending this one: */ + var HTTP_Adapter_Params g_http_pars; +}; + +type record HTTP_Adapter_Params { + charstring http_host, + integer http_port, + boolean use_ssl +}; + +function f_http_init(HTTP_Adapter_Params pars) runs on http_CT { + map(self:HTTP, system:HTTP); + g_http_pars := pars; +} + +template (value) Connect ts_HTTP_Connect(template (value) charstring hostname, + template (value) integer http_port := 80, + template (value) boolean use_ssl := false) := { + hostname := hostname, + portnumber := http_port, + use_ssl := use_ssl +} +template (value) Close ts_HTTP_Close(template (omit) integer client_id := omit) := { client_id := client_id }; + +/* function to add HeaderLines to a an existing set of HeaderLines. HeaderLines that are already present, are updated. */ +function f_overlay_HTTP_Header(HeaderLines hdr, HeaderLines additional_hdr) return template (value) HeaderLines +{ + var integer i; + var integer k; + var boolean updated; + + for (i := 0; i < lengthof(additional_hdr); i := i+1) { + updated := false; + for (k := 0; k < lengthof(hdr); k := k+1) { + if (f_str_tolower(hdr[k].header_name) == f_str_tolower(additional_hdr[i].header_name)) { + hdr[k] := additional_hdr[i]; + updated := true; + } + } + if (updated == false) { + hdr := hdr & { additional_hdr[i] }; + } + } + + return hdr; +} + +template (value) HeaderLine ts_HeaderLine(charstring header_name, charstring header_value) := { + header_name := header_name, + header_value := header_value +} + +function f_ts_HTTP_Header(template (omit) charstring body := omit, + template (omit) octetstring binary_body := omit, + template (omit) charstring host := omit, + HeaderLines custom_hdr := { }) +return template (value) HeaderLines { + var HeaderLines hdr := { }; + + /* Make sure we never use body or binary_body at the same time */ + if (not istemplatekind(body, "omit") and not istemplatekind(binary_body, "omit")) { + setverdict(fail, "use wither (ascii) body or binary_body"); + } + + /* Build default header */ + if (not istemplatekind(host, "omit")) { + hdr := hdr & {valueof(ts_HeaderLine("Host", valueof(host)))}; + } + hdr := hdr & {{ header_name := "Content-Type", header_value := "application/json" }}; + if (not istemplatekind(body, "omit")) { + hdr := hdr & {valueof(ts_HeaderLine("Content-Length", int2str(lengthof(body))))}; + } + else if (not istemplatekind(binary_body, "omit")) { + hdr := hdr & {valueof(ts_HeaderLine("Content-Length", int2str(lengthof(binary_body))))}; + } + + return f_overlay_HTTP_Header(hdr, custom_hdr); +} + +function f_ts_body_or_empty(template (omit) charstring body) return template (value) charstring { + if (istemplatekind(body, "omit")) { + return ""; + } + return body; +} + +template (value) HTTPMessage ts_HTTP_Req(charstring url, + charstring method := "GET", + template (omit) charstring body := omit, + integer v_maj := 1, integer v_min := 1, + charstring host, + HeaderLines custom_hdr := { }, + template (omit) integer client_id := omit) := { + request := { + client_id := client_id, + method := method, + uri := url, + version_major := v_maj, + version_minor := v_min, + header := f_ts_HTTP_Header(body, omit, host, custom_hdr), + body := f_ts_body_or_empty(body) + } +} + +function f_ts_body_or_empty_bin(template (omit) octetstring body) return template (value) octetstring { + if (istemplatekind(body, "omit")) { + return ''O; + } + return body; +} + +template (value) HTTPMessage ts_HTTP_Req_Bin(charstring url, + charstring method := "GET", + template (omit) octetstring body := omit, + integer v_maj := 1, integer v_min := 1, + charstring host, + HeaderLines custom_hdr := { }, + template (omit) integer client_id := omit) := { + request_binary := { + client_id := client_id, + method := method, + uri := url, + version_major := v_maj, + version_minor := v_min, + header := f_ts_HTTP_Header(omit, body, host, custom_hdr), + body := f_ts_body_or_empty_bin(body) + } +} + + +template HTTPMessage tr_HTTP_Resp(template integer sts := ?) := { + response := { + client_id := ?, + version_major := ?, + version_minor := ?, + statuscode := sts, + statustext := ?, + header := ?, + body := ? + } +}; + +template HTTPMessage tr_HTTP_Resp_Bin(template integer sts := ?) := { + response_binary := { + client_id := ?, + version_major := ?, + version_minor := ?, + statuscode := sts, + statustext := ?, + header := ?, + body := ? + } +}; + +template HTTPMessage tr_HTTP_Resp2xx := tr_HTTP_Resp((200..299)); + +function f_http_tx_request(charstring url, charstring method := "GET", + template charstring body := omit, + template octetstring binary_body := omit, + HeaderLines custom_hdr := { }, + float tout := 2.0, + template integer client_id := omit) +runs on http_CT { + var Connect_result rc; + timer T := tout; + var template integer use_client_id := omit; + + /* In case the caller didn't specify a client_id, we will create a new connection. */ + if (istemplatekind(client_id, "omit")) { + HTTP.send(ts_HTTP_Connect(g_http_pars.http_host, g_http_pars.http_port, g_http_pars.use_ssl)); + T.start; + alt { + [] HTTP.receive(Connect_result:?) -> value rc; + [] HTTP.receive { + setverdict(fail, "HTTP connection to client failed"); + self.stop; + } + [] T.timeout { + setverdict(fail, "Timeout waiting for completion of HTTP connection"); + self.stop; + } + } + use_client_id := rc.client_id; + } else { + use_client_id := client_id; + } + + if (not istemplatekind(body, "omit") and istemplatekind(binary_body, "omit")) { + /* HTTP message with ASCII content */ + HTTP.send(ts_HTTP_Req(url, method, body, host := g_http_pars.http_host & ":" & int2str(g_http_pars.http_port), + custom_hdr := custom_hdr, client_id := use_client_id)); + } else if (not istemplatekind(binary_body, "omit") and istemplatekind(body, "omit")) { + /* HTTP message with binary content */ + HTTP.send(ts_HTTP_Req_Bin(url, method, binary_body, + host := g_http_pars.http_host & ":" & int2str(g_http_pars.http_port), + custom_hdr := custom_hdr, client_id := use_client_id)); + } else if (istemplatekind(binary_body, "omit") and istemplatekind(body, "omit")) { + /* HTTP message without content (e.g. a GET request) */ + HTTP.send(ts_HTTP_Req(url, method, host := g_http_pars.http_host & ":" & int2str(g_http_pars.http_port), + custom_hdr := custom_hdr, client_id := use_client_id)); + } else { + setverdict(fail, "either binary_body or body must be used (a request can contain either ASCII data or binary data, not both!"); + } +} + +function f_http_rx_response(template HTTPMessage exp := tr_HTTP_Resp2xx, + float tout := 2.0, + template integer client_id := omit, + boolean keep_connection := false) +runs on http_CT return HTTPMessage { + var HTTPMessage resp; + timer T := tout; + T.start; + alt { + [] HTTP.receive(exp) -> value resp { + setverdict(pass); + } + [] HTTP.receive(tr_HTTP_Resp) -> value resp { + setverdict(fail, "Unexpected HTTP response ", resp); + } + [] HTTP.receive(tr_HTTP_Resp_Bin) -> value resp { + setverdict(fail, "Unexpected (binary) HTTP response ", resp); + } + [] T.timeout { + setverdict(fail, "Timeout waiting for HTTP response"); + self.stop; + } + } + + if (not keep_connection) { + HTTP.send(ts_HTTP_Close(client_id)); + } + + return resp; +} + +/* run a HTTP request and return the response */ +function f_http_transact(charstring url, charstring method := "GET", + template (omit) charstring body := omit, + template (omit) octetstring binary_body := omit, + template HTTPMessage exp := tr_HTTP_Resp2xx, + float tout := 2.0, HeaderLines custom_hdr := { }, + template integer client_id := omit, + boolean keep_connection := false) +runs on http_CT return HTTPMessage { + f_http_tx_request(url, method, body, binary_body, custom_hdr, tout, client_id); + return f_http_rx_response(exp, tout, client_id, keep_connection); +} + +function f_http_client_id_from_http_response(template HTTPMessage response_http) return template integer { + if (ispresent(response_http.response_binary)) { + return response_http.response_binary.client_id; + } else if (ispresent(response_http.response)) { + return response_http.response.client_id; + } + + return omit; +} + +} |