aboutsummaryrefslogtreecommitdiff
path: root/src/backend/taler-merchant-httpd_private-post-orders.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/taler-merchant-httpd_private-post-orders.c')
-rw-r--r--src/backend/taler-merchant-httpd_private-post-orders.c348
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,