LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_testserver.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 75 85 88.2 %
Date: 2025-06-23 16:22:09 Functions: 6 6 100.0 %

          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 1.16