Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2023 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU General Public License as
7 : published by the Free Software Foundation; either version 3, or
8 : (at your option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 :
20 : /**
21 : * @file testing/testing_api_cmd_testserver.c
22 : * @brief Implement a CMD to run an Testserver service for faking the legitimation service
23 : * @author Priscilla HUANG
24 : */
25 : #include "platform.h"
26 : #include "taler/taler_json_lib.h"
27 : #include <gnunet/gnunet_curl_lib.h>
28 : #include "taler/taler_testing_lib.h"
29 : #include "taler/taler_mhd_lib.h"
30 : #include "taler_merchant_testing_lib.h"
31 : #include "taler_merchant_service.h"
32 : #include <taler/taler_exchange_service.h>
33 :
34 : /**
35 : * State for the testserver CMD.
36 : */
37 : struct TestserverState
38 : {
39 :
40 : /**
41 : * Handle to the "testserver" service.
42 : */
43 : struct MHD_Daemon *mhd;
44 :
45 : /**
46 : * Port to listen on.
47 : */
48 : uint16_t port;
49 :
50 : /**
51 : * Array where we remember all of the requests this
52 : * server answered.
53 : */
54 : struct RequestCtx **rcs;
55 :
56 : /**
57 : * Length of the @a rcs array
58 : */
59 : unsigned int rcs_length;
60 : };
61 :
62 :
63 : struct RequestCtx
64 : {
65 : /**
66 : * URL where we are redirect.
67 : */
68 : char *url;
69 :
70 : /**
71 : * http method of the webhook.
72 : */
73 : char *http_method;
74 :
75 : /**
76 : * header of the webhook.
77 : */
78 : char *header;
79 :
80 : /**
81 : * body of the webhook.
82 : */
83 : void *body;
84 :
85 : /**
86 : * size of the body
87 : */
88 : size_t body_size;
89 :
90 : /**
91 : * Set to true when we are done with the request.
92 : */
93 : bool done;
94 : };
95 :
96 :
97 : /**
98 : * A client has requested the given url using the given method
99 : * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
100 : * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
101 : * must call MHD callbacks to provide content to give back to the
102 : * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
103 : * #MHD_HTTP_NOT_FOUND, etc.).
104 : *
105 : * @param cls argument given together with the function
106 : * pointer when the handler was registered with MHD
107 : * @param connection the connection being handled
108 : * @param url the requested url
109 : * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
110 : * #MHD_HTTP_METHOD_PUT, etc.)
111 : * @param version the HTTP version string (i.e.
112 : * MHD_HTTP_VERSION_1_1)
113 : * @param upload_data the data being uploaded (excluding HEADERS,
114 : * for a POST that fits into memory and that is encoded
115 : * with a supported encoding, the POST data will NOT be
116 : * given in upload_data and is instead available as
117 : * part of MHD_get_connection_values(); very large POST
118 : * data *will* be made available incrementally in
119 : * @a upload_data)
120 : * @param[in,out] upload_data_size set initially to the size of the
121 : * @a upload_data provided; the method must update this
122 : * value to the number of bytes NOT processed;
123 : * @param[in,out] con_cls pointer that the callback can set to some
124 : * address and that will be preserved by MHD for future
125 : * calls for this request; since the access handler may
126 : * be called many times (i.e., for a PUT/POST operation
127 : * with plenty of upload data) this allows the application
128 : * to easily associate some request-specific state.
129 : * If necessary, this state can be cleaned up in the
130 : * global MHD_RequestCompletedCallback (which
131 : * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
132 : * Initially, `*con_cls` will be NULL.
133 : * @return #MHD_YES if the connection was handled successfully,
134 : * #MHD_NO if the socket must be closed due to a serious
135 : * error while handling the request
136 : */
137 : static MHD_RESULT
138 6 : handler_cb (void *cls,
139 : struct MHD_Connection *connection,
140 : const char *url,
141 : const char *method,
142 : const char *version,
143 : const char *upload_data,
144 : size_t *upload_data_size,
145 : void **con_cls)
146 : {
147 6 : struct TestserverState *ts = cls;
148 6 : struct RequestCtx *rc = *con_cls;
149 :
150 : (void) version;
151 6 : if (NULL == rc)
152 : {
153 : const char *hdr;
154 :
155 2 : rc = GNUNET_new (struct RequestCtx);
156 2 : *con_cls = rc;
157 2 : rc->http_method = GNUNET_strdup (method);
158 2 : hdr = MHD_lookup_connection_value (connection,
159 : MHD_HEADER_KIND,
160 : "Taler-test-header");
161 2 : if (NULL != hdr)
162 2 : rc->header = GNUNET_strdup (hdr);
163 2 : if (NULL != url)
164 2 : rc->url = GNUNET_strdup (url);
165 2 : GNUNET_array_append (ts->rcs,
166 : ts->rcs_length,
167 : rc);
168 2 : fprintf (stderr,
169 : "Webhook called server at `%s' with header `%s'\n",
170 : url,
171 : hdr);
172 2 : return MHD_YES;
173 : }
174 4 : if (0 == strcasecmp (method,
175 : MHD_HTTP_METHOD_GET))
176 : {
177 : json_t *reply;
178 :
179 0 : reply = GNUNET_JSON_PACK (
180 : GNUNET_JSON_pack_string (
181 : "status",
182 : "success"));
183 0 : return TALER_MHD_reply_json_steal (connection,
184 : reply,
185 : MHD_HTTP_OK);
186 : }
187 4 : if (0 != strcasecmp (method,
188 : MHD_HTTP_METHOD_POST))
189 : {
190 0 : GNUNET_break (0);
191 0 : return MHD_NO;
192 : }
193 4 : if (0 != *upload_data_size)
194 : {
195 : void *body;
196 :
197 2 : body = GNUNET_malloc (rc->body_size + *upload_data_size);
198 2 : GNUNET_memcpy (body,
199 : rc->body,
200 : rc->body_size);
201 2 : GNUNET_free (rc->body);
202 2 : GNUNET_memcpy (body + rc->body_size,
203 : upload_data,
204 : *upload_data_size);
205 2 : rc->body = body;
206 2 : rc->body_size += *upload_data_size;
207 2 : *upload_data_size = 0;
208 2 : return MHD_YES;
209 : }
210 :
211 : {
212 : json_t *reply;
213 :
214 2 : reply = GNUNET_JSON_PACK (
215 : GNUNET_JSON_pack_string ("something",
216 : "good"));
217 2 : return TALER_MHD_reply_json_steal (connection,
218 : reply,
219 : MHD_HTTP_OK);
220 : }
221 : }
222 :
223 :
224 : static void
225 2 : cleanup (void *cls,
226 : struct MHD_Connection *connection,
227 : void **con_cls,
228 : enum MHD_RequestTerminationCode toe)
229 : {
230 2 : struct RequestCtx *rc = *con_cls;
231 :
232 : (void) cls;
233 : (void) connection;
234 : (void) toe;
235 2 : if (NULL == rc)
236 0 : return;
237 2 : rc->done = true;
238 : }
239 :
240 :
241 : /**
242 : * Run the command.
243 : *
244 : * @param cls closure.
245 : * @param cmd the command to execute.
246 : * @param is the interpreter state.
247 : */
248 : static void
249 2 : testserver_run (void *cls,
250 : const struct TALER_TESTING_Command *cmd,
251 : struct TALER_TESTING_Interpreter *is)
252 : {
253 2 : struct TestserverState *ser = cls;
254 :
255 : (void) cmd;
256 4 : ser->mhd = MHD_start_daemon (MHD_USE_AUTO_INTERNAL_THREAD,
257 2 : ser->port,
258 : NULL, NULL,
259 : &handler_cb, ser,
260 : MHD_OPTION_NOTIFY_COMPLETED, &cleanup, NULL,
261 : NULL);
262 2 : if (NULL == ser->mhd)
263 : {
264 0 : GNUNET_break (0);
265 0 : TALER_TESTING_interpreter_fail (is);
266 0 : return;
267 : }
268 2 : TALER_TESTING_interpreter_next (is);
269 : }
270 :
271 :
272 : /**
273 : * Cleanup the state from a "testserver" CMD, and possibly cancel a operation
274 : * thereof.
275 : *
276 : * @param cls closure.
277 : * @param cmd the command which is being cleaned up.
278 : */
279 : static void
280 2 : testserver_cleanup (void *cls,
281 : const struct TALER_TESTING_Command *cmd)
282 : {
283 2 : struct TestserverState *ser = cls;
284 :
285 : (void) cmd;
286 2 : if (NULL != ser->mhd)
287 : {
288 2 : MHD_stop_daemon (ser->mhd);
289 2 : ser->mhd = NULL;
290 : }
291 4 : for (unsigned int i = 0; i<ser->rcs_length; i++)
292 : {
293 2 : struct RequestCtx *rc = ser->rcs[i];
294 :
295 2 : GNUNET_assert (rc->done);
296 2 : GNUNET_free (rc->url);
297 2 : GNUNET_free (rc->http_method);
298 2 : GNUNET_free (rc->header);
299 2 : GNUNET_free (rc->body);
300 2 : GNUNET_free (rc);
301 : }
302 2 : GNUNET_array_grow (ser->rcs,
303 : ser->rcs_length,
304 : 0);
305 2 : GNUNET_free (ser);
306 2 : }
307 :
308 :
309 : static enum GNUNET_GenericReturnValue
310 10 : traits_testserver (void *cls,
311 : const void **ret,
312 : const char *trait,
313 : unsigned int index)
314 : {
315 10 : struct TestserverState *ser = cls;
316 :
317 10 : if (index >= ser->rcs_length)
318 0 : return GNUNET_NO;
319 :
320 : {
321 10 : const struct RequestCtx *rc = ser->rcs[index];
322 : struct TALER_TESTING_Trait traits[] = {
323 10 : TALER_TESTING_make_trait_urls (index,
324 10 : rc->url),
325 10 : TALER_TESTING_make_trait_http_methods (index,
326 10 : rc->http_method),
327 10 : TALER_TESTING_make_trait_http_header (index,
328 10 : rc->header),
329 10 : TALER_TESTING_make_trait_http_body (index,
330 10 : rc->body),
331 10 : TALER_TESTING_make_trait_http_body_size (index,
332 : &rc->body_size),
333 10 : TALER_TESTING_trait_end (),
334 : };
335 :
336 10 : if (! rc->done)
337 0 : return GNUNET_NO;
338 10 : return TALER_TESTING_get_trait (traits,
339 : ret,
340 : trait,
341 : index);
342 : }
343 : }
344 :
345 :
346 : /**
347 : * This function is used to start the web server.
348 : *
349 : * @param label command label
350 : * @param port is the port of the web server
351 : */
352 : struct TALER_TESTING_Command
353 2 : TALER_TESTING_cmd_testserver (const char *label,
354 : uint16_t port)
355 : {
356 : struct TestserverState *ser;
357 :
358 2 : ser = GNUNET_new (struct TestserverState);
359 2 : ser->port = port;
360 : {
361 2 : struct TALER_TESTING_Command cmd = {
362 : .cls = ser,
363 : .label = label,
364 : .run = &testserver_run,
365 : .cleanup = &testserver_cleanup,
366 : .traits = &traits_testserver
367 : };
368 :
369 2 : return cmd;
370 : }
371 : }
372 :
373 :
374 : /* end of testing_api_cmd_checkserver.c */
|