LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_testserver.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 88.2 % 85 75
Test Date: 2025-11-13 17:46:01 Functions: 100.0 % 6 6

            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 */
        

Generated by: LCOV version 2.0-1