From 3e4fa16134ff8a08a70c3041bb008a493760fad4 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 31 Oct 2019 20:17:23 +0100 Subject: prepare data structures and trigger logic for long polling in check-payment processing --- contrib/uncrustify.cfg | 2 + src/backend/taler-merchant-httpd.c | 110 +++++++++++++++++++++++++++++++-- src/backend/taler-merchant-httpd.h | 49 +++++++++++++++ src/backend/taler-merchant-httpd_pay.c | 60 +++++++++++++++++- 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, @@ -208,6 +222,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 @@ -266,6 +266,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. @@ -290,6 +313,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? */ @@ -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 @@ -412,6 +412,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 @@ -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)); -- cgit v1.2.3