LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_reserves_get.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 57 71 80.3 %
Date: 2025-06-22 12:09:43 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2024 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Affero General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Affero General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-exchange-httpd_reserves_get.c
      18             :  * @brief Handle /reserves/$RESERVE_PUB GET requests
      19             :  * @author Florian Dold
      20             :  * @author Benedikt Mueller
      21             :  * @author Christian Grothoff
      22             :  */
      23             : #include "taler/platform.h"
      24             : #include <gnunet/gnunet_util_lib.h>
      25             : #include <jansson.h>
      26             : #include "taler/taler_mhd_lib.h"
      27             : #include "taler/taler_json_lib.h"
      28             : #include "taler/taler_dbevents.h"
      29             : #include "taler-exchange-httpd_keys.h"
      30             : #include "taler-exchange-httpd_reserves_get.h"
      31             : #include "taler-exchange-httpd_responses.h"
      32             : 
      33             : 
      34             : /**
      35             :  * Reserve GET request that is long-polling.
      36             :  */
      37             : struct ReservePoller
      38             : {
      39             :   /**
      40             :    * Kept in a DLL.
      41             :    */
      42             :   struct ReservePoller *next;
      43             : 
      44             :   /**
      45             :    * Kept in a DLL.
      46             :    */
      47             :   struct ReservePoller *prev;
      48             : 
      49             :   /**
      50             :    * Connection we are handling.
      51             :    */
      52             :   struct MHD_Connection *connection;
      53             : 
      54             :   /**
      55             :    * Our request context.
      56             :    */
      57             :   struct TEH_RequestContext *rc;
      58             : 
      59             :   /**
      60             :    * Subscription for the database event we are waiting for.
      61             :    */
      62             :   struct GNUNET_DB_EventHandler *eh;
      63             : 
      64             :   /**
      65             :    * When will this request time out?
      66             :    */
      67             :   struct GNUNET_TIME_Absolute timeout;
      68             : 
      69             :   /**
      70             :    * Public key of the reserve the inquiry is about.
      71             :    */
      72             :   struct TALER_ReservePublicKeyP reserve_pub;
      73             : 
      74             :   /**
      75             :    * Balance of the reserve, set in the callback.
      76             :    */
      77             :   struct TALER_Amount balance;
      78             : 
      79             :   /**
      80             :    * Last origin account of the reserve, NULL if only
      81             :    * P2P payments were made.
      82             :    */
      83             :   struct TALER_FullPayto origin_account;
      84             : 
      85             :   /**
      86             :    * True if we are still suspended.
      87             :    */
      88             :   bool suspended;
      89             : 
      90             : };
      91             : 
      92             : 
      93             : /**
      94             :  * Head of list of requests in long polling.
      95             :  */
      96             : static struct ReservePoller *rp_head;
      97             : 
      98             : /**
      99             :  * Tail of list of requests in long polling.
     100             :  */
     101             : static struct ReservePoller *rp_tail;
     102             : 
     103             : 
     104             : void
     105          21 : TEH_reserves_get_cleanup ()
     106             : {
     107          21 :   for (struct ReservePoller *rp = rp_head;
     108          21 :        NULL != rp;
     109           0 :        rp = rp->next)
     110             :   {
     111           0 :     if (rp->suspended)
     112             :     {
     113           0 :       rp->suspended = false;
     114           0 :       MHD_resume_connection (rp->connection);
     115             :     }
     116             :   }
     117          21 : }
     118             : 
     119             : 
     120             : /**
     121             :  * Function called once a connection is done to
     122             :  * clean up the `struct ReservePoller` state.
     123             :  *
     124             :  * @param rc context to clean up for
     125             :  */
     126             : static void
     127          34 : rp_cleanup (struct TEH_RequestContext *rc)
     128             : {
     129          34 :   struct ReservePoller *rp = rc->rh_ctx;
     130             : 
     131          34 :   GNUNET_assert (! rp->suspended);
     132          34 :   if (NULL != rp->eh)
     133             :   {
     134          11 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     135             :                 "Cancelling DB event listening on cleanup (odd unless during shutdown)\n");
     136          11 :     TEH_plugin->event_listen_cancel (TEH_plugin->cls,
     137             :                                      rp->eh);
     138          11 :     rp->eh = NULL;
     139             :   }
     140          34 :   GNUNET_CONTAINER_DLL_remove (rp_head,
     141             :                                rp_tail,
     142             :                                rp);
     143          34 :   GNUNET_free (rp->origin_account.full_payto);
     144          34 :   GNUNET_free (rp);
     145          34 : }
     146             : 
     147             : 
     148             : /**
     149             :  * Function called on events received from Postgres.
     150             :  * Wakes up long pollers.
     151             :  *
     152             :  * @param cls the `struct TEH_RequestContext *`
     153             :  * @param extra additional event data provided
     154             :  * @param extra_size number of bytes in @a extra
     155             :  */
     156             : static void
     157           9 : db_event_cb (void *cls,
     158             :              const void *extra,
     159             :              size_t extra_size)
     160             : {
     161           9 :   struct ReservePoller *rp = cls;
     162             :   struct GNUNET_AsyncScopeSave old_scope;
     163             : 
     164             :   (void) extra;
     165             :   (void) extra_size;
     166           9 :   if (! rp->suspended)
     167           0 :     return; /* might get multiple wake-up events */
     168           9 :   GNUNET_async_scope_enter (&rp->rc->async_scope_id,
     169             :                             &old_scope);
     170           9 :   TEH_check_invariants ();
     171           9 :   rp->suspended = false;
     172           9 :   MHD_resume_connection (rp->connection);
     173           9 :   TALER_MHD_daemon_trigger ();
     174           9 :   TEH_check_invariants ();
     175           9 :   GNUNET_async_scope_restore (&old_scope);
     176             : }
     177             : 
     178             : 
     179             : MHD_RESULT
     180          43 : TEH_handler_reserves_get (
     181             :   struct TEH_RequestContext *rc,
     182             :   const struct TALER_ReservePublicKeyP *reserve_pub)
     183             : {
     184          43 :   struct ReservePoller *rp = rc->rh_ctx;
     185             : 
     186          43 :   if (NULL == rp)
     187             :   {
     188          34 :     rp = GNUNET_new (struct ReservePoller);
     189          34 :     rp->connection = rc->connection;
     190          34 :     rp->rc = rc;
     191          34 :     rc->rh_ctx = rp;
     192          34 :     rc->rh_cleaner = &rp_cleanup;
     193          34 :     GNUNET_CONTAINER_DLL_insert (rp_head,
     194             :                                  rp_tail,
     195             :                                  rp);
     196          34 :     rp->reserve_pub = *reserve_pub;
     197          34 :     TALER_MHD_parse_request_timeout (rc->connection,
     198             :                                      &rp->timeout);
     199             :   }
     200             : 
     201          43 :   if ( (GNUNET_TIME_absolute_is_future (rp->timeout)) &&
     202          20 :        (NULL == rp->eh) )
     203             :   {
     204          11 :     struct TALER_ReserveEventP rep = {
     205          11 :       .header.size = htons (sizeof (rep)),
     206          11 :       .header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
     207             :       .reserve_pub = rp->reserve_pub
     208             :     };
     209             : 
     210          11 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     211             :                 "Starting DB event listening until %s\n",
     212             :                 GNUNET_TIME_absolute2s (rp->timeout));
     213          22 :     rp->eh = TEH_plugin->event_listen (
     214          11 :       TEH_plugin->cls,
     215             :       GNUNET_TIME_absolute_get_remaining (rp->timeout),
     216             :       &rep.header,
     217             :       &db_event_cb,
     218             :       rp);
     219             :   }
     220             :   {
     221             :     enum GNUNET_DB_QueryStatus qs;
     222             : 
     223          43 :     GNUNET_free (rp->origin_account.full_payto);
     224          43 :     qs = TEH_plugin->get_reserve_balance (TEH_plugin->cls,
     225          43 :                                           &rp->reserve_pub,
     226             :                                           &rp->balance,
     227             :                                           &rp->origin_account);
     228          43 :     switch (qs)
     229             :     {
     230           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
     231           0 :       GNUNET_break (0); /* single-shot query should never have soft-errors */
     232           0 :       return TALER_MHD_reply_with_error (rc->connection,
     233             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     234             :                                          TALER_EC_GENERIC_DB_SOFT_FAILURE,
     235             :                                          "get_reserve_balance");
     236           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     237           0 :       GNUNET_break (0);
     238           0 :       return TALER_MHD_reply_with_error (rc->connection,
     239             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     240             :                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
     241             :                                          "get_reserve_balance");
     242          34 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     243          34 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     244             :                   "Got reserve balance of %s\n",
     245             :                   TALER_amount2s (&rp->balance));
     246          34 :       return TALER_MHD_REPLY_JSON_PACK (
     247             :         rc->connection,
     248             :         MHD_HTTP_OK,
     249             :         GNUNET_JSON_pack_allow_null (
     250             :           GNUNET_JSON_pack_string (
     251             :             "last_origin",
     252             :             rp->origin_account.full_payto)),
     253             :         TALER_JSON_pack_amount ("balance",
     254             :                                 &rp->balance));
     255           9 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     256           9 :       if (! GNUNET_TIME_absolute_is_future (rp->timeout))
     257             :       {
     258           0 :         return TALER_MHD_reply_with_error (rc->connection,
     259             :                                            MHD_HTTP_NOT_FOUND,
     260             :                                            TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
     261             :                                            NULL);
     262             :       }
     263           9 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     264             :                   "Long-polling on reserve for %s\n",
     265             :                   GNUNET_STRINGS_relative_time_to_string (
     266             :                     GNUNET_TIME_absolute_get_remaining (rp->timeout),
     267             :                     true));
     268           9 :       rp->suspended = true;
     269           9 :       MHD_suspend_connection (rc->connection);
     270           9 :       return MHD_YES;
     271             :     }
     272             :   }
     273           0 :   GNUNET_break (0);
     274           0 :   return MHD_NO;
     275             : }
     276             : 
     277             : 
     278             : /* end of taler-exchange-httpd_reserves_get.c */

Generated by: LCOV version 1.16