merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

testing_api_cmd_testserver.c (10006B)


      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 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   struct TestserverState *ts = cls;
    148   struct RequestCtx *rc = *con_cls;
    149 
    150   (void) version;
    151   if (NULL == rc)
    152   {
    153     const char *hdr;
    154 
    155     rc = GNUNET_new (struct RequestCtx);
    156     *con_cls = rc;
    157     rc->http_method = GNUNET_strdup (method);
    158     hdr = MHD_lookup_connection_value (connection,
    159                                        MHD_HEADER_KIND,
    160                                        "Taler-test-header");
    161     if (NULL != hdr)
    162       rc->header = GNUNET_strdup (hdr);
    163     if (NULL != url)
    164       rc->url = GNUNET_strdup (url);
    165     GNUNET_array_append (ts->rcs,
    166                          ts->rcs_length,
    167                          rc);
    168     fprintf (stderr,
    169              "Webhook called server at `%s' with header `%s'\n",
    170              url,
    171              hdr);
    172     return MHD_YES;
    173   }
    174   if (0 == strcasecmp (method,
    175                        MHD_HTTP_METHOD_GET))
    176   {
    177     json_t *reply;
    178 
    179     reply = GNUNET_JSON_PACK (
    180       GNUNET_JSON_pack_string (
    181         "status",
    182         "success"));
    183     return TALER_MHD_reply_json_steal (connection,
    184                                        reply,
    185                                        MHD_HTTP_OK);
    186   }
    187   if (0 != strcasecmp (method,
    188                        MHD_HTTP_METHOD_POST))
    189   {
    190     GNUNET_break (0);
    191     return MHD_NO;
    192   }
    193   if (0 != *upload_data_size)
    194   {
    195     void *body;
    196 
    197     body = GNUNET_malloc (rc->body_size + *upload_data_size);
    198     GNUNET_memcpy (body,
    199                    rc->body,
    200                    rc->body_size);
    201     GNUNET_free (rc->body);
    202     GNUNET_memcpy (body + rc->body_size,
    203                    upload_data,
    204                    *upload_data_size);
    205     rc->body = body;
    206     rc->body_size += *upload_data_size;
    207     *upload_data_size = 0;
    208     return MHD_YES;
    209   }
    210 
    211   {
    212     json_t *reply;
    213 
    214     reply = GNUNET_JSON_PACK (
    215       GNUNET_JSON_pack_string ("something",
    216                                "good"));
    217     return TALER_MHD_reply_json_steal (connection,
    218                                        reply,
    219                                        MHD_HTTP_OK);
    220   }
    221 }
    222 
    223 
    224 static void
    225 cleanup (void *cls,
    226          struct MHD_Connection *connection,
    227          void **con_cls,
    228          enum MHD_RequestTerminationCode toe)
    229 {
    230   struct RequestCtx *rc = *con_cls;
    231 
    232   (void) cls;
    233   (void) connection;
    234   (void) toe;
    235   if (NULL == rc)
    236     return;
    237   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 testserver_run (void *cls,
    250                 const struct TALER_TESTING_Command *cmd,
    251                 struct TALER_TESTING_Interpreter *is)
    252 {
    253   struct TestserverState *ser = cls;
    254 
    255   (void) cmd;
    256   ser->mhd = MHD_start_daemon (MHD_USE_AUTO_INTERNAL_THREAD,
    257                                ser->port,
    258                                NULL, NULL,
    259                                &handler_cb, ser,
    260                                MHD_OPTION_NOTIFY_COMPLETED, &cleanup, NULL,
    261                                NULL);
    262   if (NULL == ser->mhd)
    263   {
    264     GNUNET_break (0);
    265     TALER_TESTING_interpreter_fail (is);
    266     return;
    267   }
    268   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 testserver_cleanup (void *cls,
    281                     const struct TALER_TESTING_Command *cmd)
    282 {
    283   struct TestserverState *ser = cls;
    284 
    285   (void) cmd;
    286   if (NULL != ser->mhd)
    287   {
    288     MHD_stop_daemon (ser->mhd);
    289     ser->mhd = NULL;
    290   }
    291   for (unsigned int i = 0; i<ser->rcs_length; i++)
    292   {
    293     struct RequestCtx *rc = ser->rcs[i];
    294 
    295     GNUNET_assert (rc->done);
    296     GNUNET_free (rc->url);
    297     GNUNET_free (rc->http_method);
    298     GNUNET_free (rc->header);
    299     GNUNET_free (rc->body);
    300     GNUNET_free (rc);
    301   }
    302   GNUNET_array_grow (ser->rcs,
    303                      ser->rcs_length,
    304                      0);
    305   GNUNET_free (ser);
    306 }
    307 
    308 
    309 static enum GNUNET_GenericReturnValue
    310 traits_testserver (void *cls,
    311                    const void **ret,
    312                    const char *trait,
    313                    unsigned int index)
    314 {
    315   struct TestserverState *ser = cls;
    316 
    317   if (index >= ser->rcs_length)
    318     return GNUNET_NO;
    319 
    320   {
    321     const struct RequestCtx *rc = ser->rcs[index];
    322     struct TALER_TESTING_Trait traits[] = {
    323       TALER_TESTING_make_trait_urls (index,
    324                                      rc->url),
    325       TALER_TESTING_make_trait_http_methods (index,
    326                                              rc->http_method),
    327       TALER_TESTING_make_trait_http_header (index,
    328                                             rc->header),
    329       TALER_TESTING_make_trait_http_body (index,
    330                                           rc->body),
    331       TALER_TESTING_make_trait_http_body_size (index,
    332                                                &rc->body_size),
    333       TALER_TESTING_trait_end (),
    334     };
    335 
    336     if (! rc->done)
    337       return GNUNET_NO;
    338     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 TALER_TESTING_cmd_testserver (const char *label,
    354                               uint16_t port)
    355 {
    356   struct TestserverState *ser;
    357 
    358   ser = GNUNET_new (struct TestserverState);
    359   ser->port = port;
    360   {
    361     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     return cmd;
    370   }
    371 }
    372 
    373 
    374 /* end of testing_api_cmd_checkserver.c */