blob: 97233e50327bf1e441b834265c2a8b8f228f5452 [file] [log] [blame]
Harald Welte55e879c2021-02-19 13:16:28 +01001module HTTP_Adapter {
2
3/* HTTP Adapter component, originally part of Integration Tests for osmo-remsim-server
4 * (C) 2019 by Harald Welte <laforge@gnumonks.org>
5 * All rights reserved.
6 *
7 * Released under the terms of GNU General Public License, Version 2 or
8 * (at your option) any later version.
9 *
10 * SPDX-License-Identifier: GPL-2.0-or-later
11 *
12 * This test suite tests osmo-remsim-server by attaching to the external interfaces
13 * such as RSPRO for simulated clients + bankds and RSRES (REST backend interface).
14 */
15
16import from HTTPmsg_Types all;
17import from HTTPmsg_PortType all;
Philipp Maier6406e6d2024-03-26 13:37:11 +010018import from Native_Functions all;
Harald Welte55e879c2021-02-19 13:16:28 +010019
20type component http_CT {
21 port HTTPmsg_PT HTTP;
Philipp Maier260f7082024-04-19 13:12:49 +020022 var HTTP_Adapter_Params g_pars;
Harald Welte55e879c2021-02-19 13:16:28 +010023};
24
Philipp Maier260f7082024-04-19 13:12:49 +020025type record HTTP_Adapter_Params {
26 charstring http_host,
Philipp Maier832b1ef2024-03-26 17:12:37 +010027 integer http_port,
28 boolean use_ssl
Philipp Maier260f7082024-04-19 13:12:49 +020029};
30
31function f_http_init(HTTP_Adapter_Params pars) runs on http_CT {
Harald Welte55e879c2021-02-19 13:16:28 +010032 map(self:HTTP, system:HTTP);
Philipp Maier260f7082024-04-19 13:12:49 +020033 g_pars := pars;
Harald Welte55e879c2021-02-19 13:16:28 +010034}
35
36template (value) Connect ts_HTTP_Connect(template (value) charstring hostname,
37 template (value) integer http_port := 80,
38 template (value) boolean use_ssl := false) := {
39 hostname := hostname,
40 portnumber := http_port,
41 use_ssl := use_ssl
42}
Philipp Maier0957b1b2024-04-25 11:38:05 +020043template (value) Close ts_HTTP_Close(template (omit) integer client_id := omit) := { client_id := client_id };
Harald Welte55e879c2021-02-19 13:16:28 +010044
Philipp Maier6406e6d2024-03-26 13:37:11 +010045/* function to add HeaderLines to a an existing set of HeaderLines. HeaderLines that are already present, are updated. */
46function f_overlay_HTTP_Header(HeaderLines hdr, HeaderLines additional_hdr) return template (value) HeaderLines
47{
48 var integer i;
49 var integer k;
50 var boolean updated;
51
52 for (i := 0; i < lengthof(additional_hdr); i := i+1) {
53 updated := false;
54 for (k := 0; k < lengthof(hdr); k := k+1) {
55 if (f_str_tolower(hdr[k].header_name) == f_str_tolower(additional_hdr[i].header_name)) {
56 hdr[k] := additional_hdr[i];
57 updated := true;
58 }
59 }
60 if (updated == false) {
61 hdr := hdr & { additional_hdr[i] };
62 }
63 }
64
65 return hdr;
66}
67
68template (value) HeaderLine ts_HeaderLine(charstring header_name, charstring header_value) := {
69 header_name := header_name,
70 header_value := header_value
71}
72
73function f_ts_HTTP_Header(template (omit) charstring body := omit,
Philipp Maier28e7ad32024-04-24 15:44:15 +020074 template (omit) octetstring binary_body := omit,
Philipp Maier6406e6d2024-03-26 13:37:11 +010075 template (omit) charstring host := omit,
76 HeaderLines custom_hdr := { })
77return template (value) HeaderLines {
78 var HeaderLines hdr := { };
79
Philipp Maier28e7ad32024-04-24 15:44:15 +020080 /* Make sure we never use body or binary_body at the same time */
81 if (not istemplatekind(body, "omit") and not istemplatekind(binary_body, "omit")) {
82 setverdict(fail, "use wither (ascii) body or binary_body");
83 }
84
Philipp Maier6406e6d2024-03-26 13:37:11 +010085 /* Build default header */
86 if (not istemplatekind(host, "omit")) {
87 hdr := hdr & {valueof(ts_HeaderLine("Host", valueof(host)))};
88 }
89 hdr := hdr & {{ header_name := "Content-Type", header_value := "application/json" }};
90 if (not istemplatekind(body, "omit")) {
91 hdr := hdr & {valueof(ts_HeaderLine("Content-Length", int2str(lengthof(body))))};
92 }
Philipp Maier28e7ad32024-04-24 15:44:15 +020093 else if (not istemplatekind(binary_body, "omit")) {
94 hdr := hdr & {valueof(ts_HeaderLine("Content-Length", int2str(lengthof(binary_body))))};
95 }
Philipp Maier6406e6d2024-03-26 13:37:11 +010096
97 return f_overlay_HTTP_Header(hdr, custom_hdr);
Harald Welte55e879c2021-02-19 13:16:28 +010098}
99
Philipp Maieref20fa02024-03-26 15:50:22 +0100100function f_ts_body_or_empty(template (omit) charstring body) return template (value) charstring {
101 if (istemplatekind(body, "omit")) {
102 return "";
103 }
104 return body;
105}
106
Harald Welte55e879c2021-02-19 13:16:28 +0100107template (value) HTTPMessage ts_HTTP_Req(charstring url,
108 charstring method := "GET",
Philipp Maieref20fa02024-03-26 15:50:22 +0100109 template (omit) charstring body := omit,
Philipp Maier4a19b472024-03-25 15:08:43 +0100110 integer v_maj := 1, integer v_min := 1,
Philipp Maier6406e6d2024-03-26 13:37:11 +0100111 charstring host,
Philipp Maier0957b1b2024-04-25 11:38:05 +0200112 HeaderLines custom_hdr := { },
113 template (omit) integer client_id := omit) := {
Harald Welte55e879c2021-02-19 13:16:28 +0100114 request := {
Philipp Maier0957b1b2024-04-25 11:38:05 +0200115 client_id := client_id,
Harald Welte55e879c2021-02-19 13:16:28 +0100116 method := method,
117 uri := url,
118 version_major := v_maj,
119 version_minor := v_min,
Philipp Maier28e7ad32024-04-24 15:44:15 +0200120 header := f_ts_HTTP_Header(body, omit, host, custom_hdr),
Philipp Maieref20fa02024-03-26 15:50:22 +0100121 body := f_ts_body_or_empty(body)
Harald Welte55e879c2021-02-19 13:16:28 +0100122 }
123}
124
Philipp Maier28e7ad32024-04-24 15:44:15 +0200125function f_ts_body_or_empty_bin(template (omit) octetstring body) return template (value) octetstring {
126 if (istemplatekind(body, "omit")) {
127 return ''O;
128 }
129 return body;
130}
131
132template (value) HTTPMessage ts_HTTP_Req_Bin(charstring url,
133 charstring method := "GET",
134 template (omit) octetstring body := omit,
135 integer v_maj := 1, integer v_min := 1,
136 charstring host,
Philipp Maier0957b1b2024-04-25 11:38:05 +0200137 HeaderLines custom_hdr := { },
138 template (omit) integer client_id := omit) := {
Philipp Maier28e7ad32024-04-24 15:44:15 +0200139 request_binary := {
Philipp Maier0957b1b2024-04-25 11:38:05 +0200140 client_id := client_id,
Philipp Maier28e7ad32024-04-24 15:44:15 +0200141 method := method,
142 uri := url,
143 version_major := v_maj,
144 version_minor := v_min,
145 header := f_ts_HTTP_Header(omit, body, host, custom_hdr),
146 body := f_ts_body_or_empty_bin(body)
147 }
148}
149
150
Harald Welte55e879c2021-02-19 13:16:28 +0100151template HTTPMessage tr_HTTP_Resp(template integer sts := ?) := {
152 response := {
153 client_id := ?,
154 version_major := ?,
155 version_minor := ?,
156 statuscode := sts,
157 statustext := ?,
158 header := ?,
159 body := ?
160 }
161};
162
Philipp Maier28e7ad32024-04-24 15:44:15 +0200163template HTTPMessage tr_HTTP_Resp_Bin(template integer sts := ?) := {
164 response_binary := {
165 client_id := ?,
166 version_major := ?,
167 version_minor := ?,
168 statuscode := sts,
169 statustext := ?,
170 header := ?,
171 body := ?
172 }
173};
174
Harald Welte55e879c2021-02-19 13:16:28 +0100175template HTTPMessage tr_HTTP_Resp2xx := tr_HTTP_Resp((200..299));
176
Philipp Maier28e7ad32024-04-24 15:44:15 +0200177function f_http_tx_request(charstring url, charstring method := "GET",
178 template charstring body := omit,
179 template octetstring binary_body := omit,
Philipp Maier0957b1b2024-04-25 11:38:05 +0200180 HeaderLines custom_hdr := { },
181 float tout := 2.0,
182 template integer client_id := omit)
Harald Welte205b5372021-02-22 09:16:21 +0100183runs on http_CT {
Philipp Maier0957b1b2024-04-25 11:38:05 +0200184 var Connect_result rc;
185 timer T := tout;
186 var template integer use_client_id := omit;
187
188 /* In case the caller didn't specify a client_id, we will create a new connection. */
189 if (istemplatekind(client_id, "omit")) {
190 HTTP.send(ts_HTTP_Connect(g_pars.http_host, g_pars.http_port, g_pars.use_ssl));
191 T.start;
192 alt {
193 [] HTTP.receive(Connect_result:?) -> value rc;
194 [] HTTP.receive {
195 setverdict(fail, "HTTP connection to client failed");
196 self.stop;
197 }
198 [] T.timeout {
199 setverdict(fail, "Timeout waiting for completion of HTTP connection");
200 self.stop;
201 }
202 }
203 use_client_id := rc.client_id;
204 } else {
205 use_client_id := client_id;
206 }
Philipp Maier28e7ad32024-04-24 15:44:15 +0200207
Philipp Maier2cb12512024-05-07 14:54:23 +0200208 if (not istemplatekind(body, "omit") and istemplatekind(binary_body, "omit")) {
209 /* HTTP message with ASCII content */
Philipp Maier0957b1b2024-04-25 11:38:05 +0200210 HTTP.send(ts_HTTP_Req(url, method, body, host := g_pars.http_host & ":" & int2str(g_pars.http_port),
211 custom_hdr := custom_hdr, client_id := use_client_id));
Philipp Maier2cb12512024-05-07 14:54:23 +0200212 } else if (not istemplatekind(binary_body, "omit") and istemplatekind(body, "omit")) {
213 /* HTTP message with binary content */
Philipp Maier0957b1b2024-04-25 11:38:05 +0200214 HTTP.send(ts_HTTP_Req_Bin(url, method, binary_body,
215 host := g_pars.http_host & ":" & int2str(g_pars.http_port),
216 custom_hdr := custom_hdr, client_id := use_client_id));
Philipp Maier2cb12512024-05-07 14:54:23 +0200217 } else if (istemplatekind(binary_body, "omit") and istemplatekind(body, "omit")) {
218 /* HTTP message without content (e.g. a GET request) */
219 HTTP.send(ts_HTTP_Req(url, method, host := g_pars.http_host & ":" & int2str(g_pars.http_port),
220 custom_hdr := custom_hdr, client_id := use_client_id));
221 } else {
222 setverdict(fail, "either binary_body or body must be used (a request can contain either ASCII data or binary data, not both!");
Philipp Maier28e7ad32024-04-24 15:44:15 +0200223 }
Harald Welte205b5372021-02-22 09:16:21 +0100224}
225
Philipp Maier0957b1b2024-04-25 11:38:05 +0200226function f_http_rx_response(template HTTPMessage exp := tr_HTTP_Resp2xx,
227 float tout := 2.0,
228 template integer client_id := omit,
229 boolean keep_connection := false)
Harald Welte55e879c2021-02-19 13:16:28 +0100230runs on http_CT return HTTPMessage {
231 var HTTPMessage resp;
Harald Weltead9d8362021-02-22 10:23:00 +0100232 timer T := tout;
Harald Welte55e879c2021-02-19 13:16:28 +0100233 T.start;
234 alt {
235 [] HTTP.receive(exp) -> value resp {
236 setverdict(pass);
237 }
238 [] HTTP.receive(tr_HTTP_Resp) -> value resp {
239 setverdict(fail, "Unexpected HTTP response ", resp);
240 }
Philipp Maier28e7ad32024-04-24 15:44:15 +0200241 [] HTTP.receive(tr_HTTP_Resp_Bin) -> value resp {
242 setverdict(fail, "Unexpected (binary) HTTP response ", resp);
243 }
Harald Welte55e879c2021-02-19 13:16:28 +0100244 [] T.timeout {
245 setverdict(fail, "Timeout waiting for HTTP response");
246 self.stop;
247 }
248 }
Philipp Maier0957b1b2024-04-25 11:38:05 +0200249
250 if (not keep_connection) {
251 HTTP.send(ts_HTTP_Close(client_id));
252 }
253
Harald Welte55e879c2021-02-19 13:16:28 +0100254 return resp;
255}
256
Harald Welte205b5372021-02-22 09:16:21 +0100257/* run a HTTP request and return the response */
258function f_http_transact(charstring url, charstring method := "GET",
Philipp Maier28e7ad32024-04-24 15:44:15 +0200259 template (omit) charstring body := omit,
260 template (omit) octetstring binary_body := omit,
261 template HTTPMessage exp := tr_HTTP_Resp2xx,
Philipp Maier0957b1b2024-04-25 11:38:05 +0200262 float tout := 2.0, HeaderLines custom_hdr := { },
263 template integer client_id := omit,
264 boolean keep_connection := false)
Harald Welte205b5372021-02-22 09:16:21 +0100265runs on http_CT return HTTPMessage {
Philipp Maier0957b1b2024-04-25 11:38:05 +0200266 f_http_tx_request(url, method, body, binary_body, custom_hdr, tout, client_id);
267 return f_http_rx_response(exp, tout, client_id, keep_connection);
268}
269
270function f_http_client_id_from_http_response(template HTTPMessage response_http) return template integer {
271 if (ispresent(response_http.response_binary)) {
272 return response_http.response_binary.client_id;
273 } else if (ispresent(response_http.response)) {
274 return response_http.response.client_id;
275 }
276
277 return omit;
Harald Welte205b5372021-02-22 09:16:21 +0100278}
279
Harald Welte55e879c2021-02-19 13:16:28 +0100280}