aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2019-10-31 20:17:23 +0100
committerChristian Grothoff <christian@grothoff.org>2019-10-31 20:17:23 +0100
commit3e4fa16134ff8a08a70c3041bb008a493760fad4 (patch)
treeb0b70dd7648fe0072daeef6820250e4bbfc8277f
parent9bb0242a1daf560c33e8c346dbb92fec8d64a791 (diff)
downloadmerchant-3e4fa16134ff8a08a70c3041bb008a493760fad4.tar.gz
merchant-3e4fa16134ff8a08a70c3041bb008a493760fad4.tar.bz2
merchant-3e4fa16134ff8a08a70c3041bb008a493760fad4.zip
prepare data structures and trigger logic for long polling in check-payment processing
-rw-r--r--contrib/uncrustify.cfg2
-rw-r--r--src/backend/taler-merchant-httpd.c110
-rw-r--r--src/backend/taler-merchant-httpd.h49
-rw-r--r--src/backend/taler-merchant-httpd_pay.c60
4 files changed, 214 insertions, 7 deletions
diff --git a/contrib/uncrustify.cfg b/contrib/uncrustify.cfg
index 8c9df2c4..12dd62c4 100644
--- a/contrib/uncrustify.cfg
+++ b/contrib/uncrustify.cfg
@@ -49,6 +49,8 @@ nl_assign_brace=remove
# No extra newlines that cause noisy diffs
nl_start_of_file=remove
+nl_before_func_body_proto = 2
+nl_before_func_body_def = 3
nl_after_func_proto = 2
nl_after_func_body = 3
# If there's no new line, it's not a text file!
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 42e15ea1..db05c701 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -155,6 +155,18 @@ static char *serve_unixpath;
*/
static mode_t unixpath_mode;
+/**
+ * MIN-Heap of suspended connections to resume when the timeout expires,
+ * ordered by timeout. Values are of type `struct MHD_Connection`
+ */
+struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
+
+/**
+ * Hash map from H(order_id,merchant_pub) to `struct MHD_Connection`
+ * entries to resume when a payment is made for the given order.
+ */
+struct GNUNET_CONTAINER_MultiHashMap *payment_trigger_map;
+
/**
* Return #GNUNET_YES if given a valid correlation ID and
@@ -189,6 +201,8 @@ hashmap_free (void *cls,
struct MerchantInstance *mi = value;
struct WireMethod *wm;
+ (void) cls;
+ (void) key;
while (NULL != (wm = (mi->wm_head)))
{
GNUNET_CONTAINER_DLL_remove (mi->wm_head,
@@ -209,6 +223,59 @@ hashmap_free (void *cls,
/**
+ * Callback that frees all the elements in the #payment_trigger_map.
+ * This function should actually never be called, as by the time we
+ * get to it, all payment triggers should have been cleaned up!
+ *
+ * @param cls closure, NULL
+ * @param key current key
+ * @param value a `struct TMH_SuspendedConnection`
+ * @return #GNUNET_OK
+ */
+static int
+payment_trigger_free (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
+{
+ struct TMH_SuspendedConnection *sc = value;
+
+ (void) cls;
+ (void) key;
+ (void) sc; /* cannot really 'clean up' */
+ GNUNET_break (0);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Compute @a key to use for @a order_id and @a mpub in our
+ * #payment_trigger_map.
+ *
+ * @param order_id an order ID
+ * @param mpub an instance public key
+ * @param key[out] set to the hash map key to use
+ */
+void
+TMH_compute_pay_key (const char *order_id,
+ const struct TALER_MerchantPublicKeyP *mpub,
+ struct GNUNET_HashCode *key)
+{
+ size_t olen = strlen (order_id);
+ char buf[sizeof (*mpub) + olen];
+
+ memcpy (buf,
+ mpub,
+ sizeof (*mpub));
+ memcpy (&buf[sizeof (*mpub)],
+ order_id,
+ olen);
+ GNUNET_CRYPTO_hash (buf,
+ sizeof (buf),
+ key);
+}
+
+
+/**
* Shutdown task (magically invoked when the application is being
* quit)
*
@@ -217,6 +284,8 @@ hashmap_free (void *cls,
static void
do_shutdown (void *cls)
{
+ struct TMH_SuspendedConnection *sc;
+
MH_force_pc_resume ();
MH_force_trh_resume ();
if (NULL != mhd_task)
@@ -224,6 +293,22 @@ do_shutdown (void *cls)
GNUNET_SCHEDULER_cancel (mhd_task);
mhd_task = NULL;
}
+ /* resume all suspended connections, must be done before stopping #mhd */
+ if (NULL != resume_timeout_heap)
+ {
+ while (NULL != (sc = GNUNET_CONTAINER_heap_remove_root (
+ resume_timeout_heap)))
+ {
+ sc->hn = NULL;
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
+ &sc->key,
+ sc));
+ MHD_resume_connection (sc->con);
+ }
+ GNUNET_CONTAINER_heap_destroy (resume_timeout_heap);
+ resume_timeout_heap = NULL;
+ }
if (NULL != mhd)
{
MHD_stop_daemon (mhd);
@@ -236,12 +321,19 @@ do_shutdown (void *cls)
}
TMH_EXCHANGES_done ();
TMH_AUDITORS_done ();
-
- GNUNET_CONTAINER_multihashmap_iterate (by_id_map,
- &hashmap_free,
- NULL);
+ if (NULL != payment_trigger_map)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (payment_trigger_map,
+ &payment_trigger_free,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (payment_trigger_map);
+ payment_trigger_map = NULL;
+ }
if (NULL != by_id_map)
{
+ GNUNET_CONTAINER_multihashmap_iterate (by_id_map,
+ &hashmap_free,
+ NULL);
GNUNET_CONTAINER_multihashmap_destroy (by_id_map);
by_id_map = NULL;
}
@@ -291,6 +383,7 @@ handle_mhd_completion_callback (void *cls,
* starts the task waiting for them.
*/
static struct GNUNET_SCHEDULER_Task *
+
prepare_daemon (void);
@@ -347,6 +440,8 @@ TMH_trigger_daemon ()
* @param daemon_handle HTTP server to prepare to run
*/
static struct GNUNET_SCHEDULER_Task *
+
+
prepare_daemon ()
{
struct GNUNET_SCHEDULER_Task *ret;
@@ -938,6 +1033,8 @@ instances_iterator_cb (void *cls,
* @return NULL if that instance is unknown to us
*/
static struct MerchantInstance *
+
+
lookup_instance (const char *instance_id)
{
struct GNUNET_HashCode h_instance;
@@ -1704,6 +1801,11 @@ run (void *cls,
GNUNET_assert (0);
}
}
+ resume_timeout_heap
+ = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
+ payment_trigger_map
+ = GNUNET_CONTAINER_multihashmap_create (16,
+ GNUNET_YES);
mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK,
port,
NULL, NULL,
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
index 0deaf170..c166efb4 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -267,6 +267,29 @@ struct TM_HandlerContext
/**
+ * Entry in a #resume_timeout_heap.
+ */
+struct TMH_SuspendedConnection
+{
+ /**
+ * Which connection was suspended.
+ */
+ struct MHD_Connection *con;
+
+ /**
+ * Key of this entry in the #payment_trigger_map
+ */
+ struct GNUNET_HashCode key;
+
+ /**
+ * Associated heap node.
+ */
+ struct GNUNET_CONTAINER_HeapNode *hn;
+
+};
+
+
+/**
* Locations from the configuration. Mapping from
* label to location data.
*/
@@ -291,6 +314,18 @@ extern struct TALER_Amount default_max_deposit_fee;
extern unsigned long long default_wire_fee_amortization;
/**
+ * MIN-Heap of suspended connections to resume when the timeout expires,
+ * ordered by timeout. Values are of type `struct TMH_SuspendedConnection`
+ */
+extern struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
+
+/**
+ * Hash map from H(order_id,merchant_pub) to `struct TMH_SuspendedConnection`
+ * entries to resume when a payment is made for the given order.
+ */
+extern struct GNUNET_CONTAINER_MultiHashMap *payment_trigger_map;
+
+/**
* Which currency do we use?
*/
extern char *TMH_currency;
@@ -348,4 +383,18 @@ extern struct GNUNET_TIME_Relative default_pay_deadline;
void
TMH_trigger_daemon (void);
+/**
+ * Compute @a key to use for @a order_id and @a mpub in our
+ * #payment_trigger_map.
+ *
+ * @param order_id an order ID
+ * @param mpub an instance public key
+ * @param key[out] set to the hash map key to use
+ */
+void
+TMH_compute_pay_key (const char *order_id,
+ const struct TALER_MerchantPublicKeyP *mpub,
+ struct GNUNET_HashCode *key);
+
+
#endif
diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c
index 69b8e2d4..4e19e6f0 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -413,6 +413,57 @@ MH_force_pc_resume ()
/**
+ * Function called to resume suspended connections.
+ *
+ * @param cls NULL
+ * @param key key in the #payment_trigger_map
+ * @param value a `struct TMH_SuspendedConnection` to resume
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+resume_operation (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
+{
+ struct TMH_SuspendedConnection *sc = value;
+
+ (void) cls;
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
+ key,
+ sc));
+ GNUNET_assert (sc ==
+ GNUNET_CONTAINER_heap_remove_node (sc->hn));
+ sc->hn = NULL;
+ MHD_resume_connection (sc->con);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Find out if we have any clients long-polling for @a order_id to be
+ * confirmed at merchant @a mpub, and if so, tell them to resume.
+ *
+ * @param order_id the order that was paid
+ * @param mpub the merchant's public key of the instance where the payment happened
+ */
+static void
+resume_suspended_payment_checks (const char *order_id,
+ const struct TALER_MerchantPublicKeyP *mpub)
+{
+ struct GNUNET_HashCode key;
+
+ TMH_compute_pay_key (order_id,
+ mpub,
+ &key);
+ GNUNET_CONTAINER_multihashmap_get_multiple (payment_trigger_map,
+ &key,
+ &resume_operation,
+ NULL);
+}
+
+
+/**
* Resume the given pay context and send the given response.
* Stores the response in the @a pc and signals MHD to resume
* the connection. Also ensures MHD runs immediately.
@@ -473,6 +524,8 @@ abort_deposit (struct PayContext *pc)
* @return the mhd response
*/
static struct MHD_Response *
+
+
sign_success_response (struct PayContext *pc)
{
json_t *refunds;
@@ -609,6 +662,8 @@ pay_context_cleanup (struct TM_HandlerContext *hc)
* @return taler error code, #TALER_EC_NONE if amount is sufficient
*/
static enum TALER_ErrorCode
+
+
check_payment_sufficient (struct PayContext *pc)
{
struct TALER_Amount acc_fee;
@@ -921,7 +976,6 @@ generate_error_response (struct PayContext *pc,
static void
find_next_exchange (struct PayContext *pc);
-
/**
* Begin of the DB transaction. If required (from
* soft/serialization errors), the transaction can be
@@ -2058,7 +2112,6 @@ begin_transaction (struct PayContext *pc)
}
/* Now commit! */
-
if (0 <= qs)
qs = db->commit (db->cls);
else
@@ -2079,7 +2132,8 @@ begin_transaction (struct PayContext *pc)
return;
}
-
+ resume_suspended_payment_checks (pc->order_id,
+ &pc->mi->pubkey);
resume_pay_with_response (pc,
MHD_HTTP_OK,
sign_success_response (pc));