diff options
Diffstat (limited to 'src/backend/taler-merchant-httpd_private-post-orders.c')
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-orders.c | 348 |
1 files changed, 315 insertions, 33 deletions
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c index 5646af4d..66cf71a5 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders.c +++ b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -94,6 +94,40 @@ struct InventoryProduct /** + * Handle for a rekey operation where we (re)request + * the /keys from the exchange. + */ +struct RekeyExchange +{ + /** + * Kept in a DLL. + */ + struct RekeyExchange *prev; + + /** + * Kept in a DLL. + */ + struct RekeyExchange *next; + + /** + * order this is for. + */ + struct OrderContext *oc; + + /** + * Base URL of the exchange. + */ + char *url; + + /** + * Request for keys. + */ + struct TMH_EXCHANGES_KeysOperation *fo; + +}; + + +/** * Information we keep per order we are processing. */ struct OrderContext @@ -105,6 +139,16 @@ struct OrderContext struct MHD_Connection *connection; /** + * Kept in a DLL while suspended. + */ + struct OrderContext *next; + + /** + * Kept in a DLL while suspended. + */ + struct OrderContext *prev; + + /** * Handler context for the request. */ struct TMH_HandlerContext *hc; @@ -132,6 +176,12 @@ struct OrderContext json_t *order; /** + * Array of exchanges we find acceptable for this + * order. + */ + json_t *exchanges; + + /** * RFC8905 payment target type to find a matching merchant account */ const char *payment_target; @@ -143,6 +193,18 @@ struct OrderContext const struct TMH_WireMethod *wm; /** + * Forced requests to /keys to update our exchange + * information. + */ + struct RekeyExchange *pending_reload_head; + + /** + * Forced requests to /keys to update our exchange + * information. + */ + struct RekeyExchange *pending_reload_tail; + + /** * Claim token for the request. */ struct TALER_ClaimTokenP claim_token; @@ -158,14 +220,19 @@ struct OrderContext struct InventoryProduct *inventory_products; /** - * Length of the @e uuids array. + * array of UUIDs used to reserve products from @a inventory_products. */ - unsigned int uuids_length; + struct GNUNET_Uuid *uuids; /** - * array of UUIDs used to reserve products from @a inventory_products. + * Shared key to use with @e pos_algorithm. */ - struct GNUNET_Uuid *uuids; + const char *pos_key; + + /** + * Our order ID. Pointer into @e order. + */ + const char *order_id; /** * which product (by offset) is out of stock, UINT_MAX if all were in-stock @@ -173,14 +240,14 @@ struct OrderContext unsigned int out_of_stock_index; /** - * Shared key to use with @e pos_algorithm. + * Length of the @e uuids array. */ - const char *pos_key; + unsigned int uuids_length; /** - * Our order ID. Pointer into @e order. + * #GNUNET_YES if suspended. */ - const char *order_id; + enum GNUNET_GenericReturnValue suspended; /** * Selected algorithm (by template) when we are to @@ -212,10 +279,50 @@ struct OrderContext ORDER_PHASE_FINISHED_MHD_NO } phase; + /** + * Set to true once we are sure that we have at + * least one good exchange. + */ + bool exchange_good; + + /** + * Did we previously force reloading of /keys from + * all exchanges? Set to 'true' to prevent us from + * doing it again (and again...). + */ + bool forced_reload; + }; /** + * Kept in a DLL while suspended. + */ +static struct OrderContext *oc_head; + +/** + * Kept in a DLL while suspended. + */ +static struct OrderContext *oc_tail; + + +void +TMH_force_orders_resume () +{ + struct OrderContext *oc; + + while (NULL != (oc = oc_head)) + { + GNUNET_CONTAINER_DLL_remove (oc_head, + oc_tail, + oc); + oc->suspended = GNUNET_SYSERR; + MHD_resume_connection (oc->connection); + } +} + + +/** * Update the phase of @a oc based on @a mret. * * @param[in,out] oc order to update phase for @@ -285,7 +392,22 @@ static void clean_order (void *cls) { struct OrderContext *oc = cls; + struct RekeyExchange *rx; + while (NULL != (rx = oc->pending_reload_head)) + { + GNUNET_CONTAINER_DLL_remove (oc->pending_reload_head, + oc->pending_reload_tail, + rx); + TMH_EXCHANGES_keys4exchange_cancel (rx->fo); + GNUNET_free (rx->url); + GNUNET_free (rx); + } + if (NULL != oc->exchanges) + { + json_decref (oc->exchanges); + oc->exchanges = NULL; + } GNUNET_array_grow (oc->inventory_products, oc->inventory_products_length, 0); @@ -734,38 +856,196 @@ check_contract (struct OrderContext *oc) /** - * Set list of acceptable exchanges in @a oc. + * Compute the set of exchanges that would be acceptable + * for this order. * - * @param[in,out] oc order context + * @param cls our `struct OrderContext` + * @param url base URL of an exchange (not used) + * @param exchange internal handle for the exchange */ static void -set_exchanges (struct OrderContext *oc) +get_acceptable (void *cls, + const char *url, + const struct TMH_Exchange *exchange) { - json_t *exchanges; + struct OrderContext *oc = cls; + unsigned int priority; + json_t *j_exchange; + enum GNUNET_GenericReturnValue res; - exchanges = TMH_exchange_get_acceptable (oc->wm); - if (0 == json_array_size (exchanges)) + res = TMH_exchange_check_debit (exchange, + oc->wm); + switch (res) + { + case GNUNET_OK: + priority = 1024; /* high */ + oc->exchange_good = true; + break; + case GNUNET_NO: + if (oc->forced_reload) + priority = 0; /* fresh negative response */ + else + priority = 512; /* stale negative response */ + break; + case GNUNET_SYSERR: + if (oc->forced_reload) + priority = 256; /* fresh, no accounts yet */ + else + priority = 768; /* stale, no accounts yet */ + break; + } + j_exchange = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("url", + url), + GNUNET_JSON_pack_uint64 ("priority", + priority), + GNUNET_JSON_pack_data_auto ("master_pub", + TMH_EXCHANGES_get_master_pub (exchange))); + GNUNET_assert (NULL != j_exchange); + GNUNET_assert (0 == + json_array_append_new (oc->exchanges, + j_exchange)); +} + + +/** + * Function called with the result of a #TMH_EXCHANGES_keys4exchange() + * operation. + * + * @param cls closure with our `struct RekeyExchange *` + * @param keys the keys of the exchange + * @param exchange representation of the exchange + */ +static void +keys_forced ( + void *cls, + struct TALER_EXCHANGE_Keys *keys, + struct TMH_Exchange *exchange) +{ + struct RekeyExchange *rx = cls; + struct OrderContext *oc = rx->oc; + + rx->fo = NULL; + GNUNET_CONTAINER_DLL_remove (oc->pending_reload_head, + oc->pending_reload_tail, + rx); + if (NULL == keys) { - json_decref (exchanges); - /* FIXME: maybe there are exchanges for which we simply - did not yet get /wire replies. We should *try* to - run find_exchange() or something to give those - exchanges a chance to respond => need to suspend! */ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Cannot create order: lacking trusted exchanges for wire method `%s'\n", - oc->wm->wire_method); - reply_with_error ( - oc, - MHD_HTTP_CONFLICT, - TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGES_FOR_WIRE_METHOD, - oc->wm->wire_method); + "Failed to download %s/keys\n", + rx->url); + } + get_acceptable (oc, + rx->url, + exchange); + GNUNET_free (rx->url); + GNUNET_free (rx); + if (NULL != oc->pending_reload_head) return; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Resuming order processing after /keys downloads (now have %u accounts)\n", + (unsigned int) json_array_size (oc->exchanges)); + GNUNET_assert (GNUNET_YES == oc->suspended); + GNUNET_CONTAINER_DLL_remove (oc_head, + oc_tail, + oc); + oc->suspended = GNUNET_NO; + MHD_resume_connection (oc->connection); + TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ +} + + +/** + * Force re-downloading of /keys from @a exchange, + * we currently have no acceptable exchange, so we + * should try to get one. + * + * @param cls closure with our `struct OrderContext` + * @param url base URL of the exchange + * @param exchange internal handle for the exchange + */ +static void +rekey_exchanges (void *cls, + const char *url, + const struct TMH_Exchange *exchange) +{ + struct OrderContext *oc = cls; + struct RekeyExchange *rx; + + rx = GNUNET_new (struct RekeyExchange); + rx->oc = oc; + rx->url = GNUNET_strdup (url); + GNUNET_CONTAINER_DLL_insert (oc->pending_reload_head, + oc->pending_reload_tail, + rx); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Forcing download of %s/keys\n", + url); + rx->fo = TMH_EXCHANGES_keys4exchange (url, + true, + &keys_forced, + rx); +} + + +/** + * Set list of acceptable exchanges in @a oc. + * + * @param[in,out] oc order context + * @return true to suspend execution + */ +static bool +set_exchanges (struct OrderContext *oc) +{ + /* Note: re-building 'oc->exchanges' every time here might be a tad + expensive; could likely consider caching the result if it starts to + matter. */ + if (NULL == oc->exchanges) + oc->exchanges = json_array (); + TMH_exchange_get_trusted (&get_acceptable, + oc); + if (! oc->exchange_good) + { + if (! oc->forced_reload) + { + oc->forced_reload = true; + GNUNET_assert (0 == + json_array_clear (oc->exchanges)); + TMH_exchange_get_trusted (&rekey_exchanges, + oc); + } + if (NULL != oc->pending_reload_head) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Still trying to re-load /keys\n"); + MHD_suspend_connection (oc->connection); + oc->suspended = GNUNET_YES; + GNUNET_CONTAINER_DLL_insert (oc_head, + oc_tail, + oc); + return true; /* reloads pending */ + } + if (0 == json_array_size (oc->exchanges)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Cannot create order: lacking trusted exchanges\n"); + reply_with_error ( + oc, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGES_FOR_WIRE_METHOD, + oc->wm->wire_method); + return false; + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Creating order, but possibly without usable trusted exchanges\n"); } + /* 'set' is correct here: reference in oc->exchanges released in cleanup_order() */ GNUNET_assert (0 == - json_object_set_new (oc->order, - "exchanges", - exchanges)); + json_object_set (oc->order, + "exchanges", + oc->exchanges)); oc->phase++; + return false; } @@ -1603,7 +1883,8 @@ TMH_private_post_orders_with_pos_secrets ( patch_order (oc); break; case ORDER_PHASE_SET_EXCHANGES: - set_exchanges (oc); + if (set_exchanges (oc)) + return MHD_YES; break; case ORDER_PHASE_CHECK_CONTRACT: check_contract (oc); @@ -1625,9 +1906,10 @@ TMH_private_post_orders_with_pos_secrets ( MHD_RESULT -TMH_private_post_orders (const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc) +TMH_private_post_orders ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) { return TMH_private_post_orders_with_pos_secrets (rh, connection, |