From 6004b808cd65c2e7bc99423d3c58d23d58995eb6 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 14 May 2017 14:13:43 +0200 Subject: fix FIXME on properly terminating MHD, resuming suspended connections before stopping the HTTPD --- src/backend/taler-merchant-httpd.c | 5 +- src/backend/taler-merchant-httpd_pay.c | 156 ++++++++++++++++++++++----------- src/backend/taler-merchant-httpd_pay.h | 11 ++- 3 files changed, 115 insertions(+), 57 deletions(-) diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 1abee1e9..0efa7eba 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -286,10 +286,7 @@ do_shutdown (void *cls) GNUNET_SCHEDULER_cancel (mhd_task); mhd_task = NULL; } - /* FIXME: MHD API requires us to resume all suspended - connections before we do this, but /pay currently - suspends connections without giving us a way to - enumerate / resume them... */ + MH_force_pc_resume (); if (NULL != mhd) { MHD_stop_daemon (mhd); diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index 5649894a..307690ea 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -118,6 +118,16 @@ struct PayContext */ struct TM_HandlerContext hc; + /** + * Stored in a DLL. + */ + struct PayContext *next; + + /** + * Stored in a DLL. + */ + struct PayContext *prev; + /** * Array with @e coins_cnt coins we are despositing. */ @@ -128,6 +138,29 @@ struct PayContext */ struct MHD_Connection *connection; + /** + * Instance of the payment's instance (in JSON format) + */ + struct MerchantInstance *mi; + + /** + * Proposal data for the proposal that is being + * payed for in this context. + */ + json_t *proposal_data; + + /** + * Task called when the (suspended) processing for + * the /pay request times out. + * Happens when we don't get a response from the exchange. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * Response to return, NULL if we don't have one yet. + */ + struct MHD_Response *response; + /** * Handle to the exchange that we are doing the payment with. * (initially NULL while @e fo is trying to find a exchange). @@ -156,6 +189,17 @@ struct PayContext */ const char *order_id; + /** + * Hashed proposal. + */ + struct GNUNET_HashCode h_proposal_data; + + /** + * "H_wire" from @e proposal_data. Used to identify the instance's + * wire transfer method. + */ + struct GNUNET_HashCode h_wire; + /** * Maximum fee the merchant is willing to pay, from @e root. * Note that IF the total fee of the exchange is higher, that is @@ -183,21 +227,19 @@ struct PayContext */ struct TALER_Amount max_wire_fee; - /** - * Number of transactions that the wire fees are expected to be - * amortized over. Never zero, defaults (conservateively) to 1. - * May be higher if merchants expect many small transactions to - * be aggregated and thus wire fees to be reasonably amortized - * due to aggregation. - */ - uint32_t wire_fee_amortization; - /** * Amount from @e root. This is the amount the merchant expects * to make, minus @e max_fee. */ struct TALER_Amount amount; + /** + * Wire transfer deadline. How soon would the merchant like the + * wire transfer to be executed? (Can be given by the frontend + * or be determined by our configuration via #wire_transfer_delay.) + */ + struct GNUNET_TIME_Absolute wire_transfer_deadline; + /** * Timestamp from @e proposal_data. */ @@ -214,27 +256,13 @@ struct PayContext struct GNUNET_TIME_Absolute pay_deadline; /** - * Hashed proposal. - */ - struct GNUNET_HashCode h_proposal_data; - - /** - * "H_wire" from @e proposal_data. Used to identify the instance's - * wire transfer method. - */ - struct GNUNET_HashCode h_wire; - - /** - * Wire transfer deadline. How soon would the merchant like the - * wire transfer to be executed? (Can be given by the frontend - * or be determined by our configuration via #wire_transfer_delay.) - */ - struct GNUNET_TIME_Absolute wire_transfer_deadline; - - /** - * Response to return, NULL if we don't have one yet. + * Number of transactions that the wire fees are expected to be + * amortized over. Never zero, defaults (conservateively) to 1. + * May be higher if merchants expect many small transactions to + * be aggregated and thus wire fees to be reasonably amortized + * due to aggregation. */ - struct MHD_Response *response; + uint32_t wire_fee_amortization; /** * Number of coins this payment is made of. Length @@ -256,13 +284,6 @@ struct PayContext */ unsigned int response_code; - /** - * Task called when the (suspended) processing for - * the /pay request times out. - * Happens when we don't get a response from the exchange. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - /** * #GNUNET_NO if the transaction is not in our database, * #GNUNET_YES if the transaction is known to our database, @@ -272,17 +293,42 @@ struct PayContext int transaction_exists; /** - * Instance of the payment's instance (in JSON format) + * #GNUNET_NO if the @e connection was not suspended, + * #GNUNET_YES if the @e connection was suspended, + * #GNUNET_SYSERR if @e connection was resumed to as + * part of #MH_force_pc_resume during shutdown. */ - struct MerchantInstance *mi; + int suspended; +}; - /** - * Proposal data for the proposal that is being - * payed for in this context. - */ - json_t *proposal_data; -}; +/** + * Head of active pay context DLL. + */ +static struct PayContext *pc_head; + +/** + * Tail of active pay context DLL. + */ +static struct PayContext *pc_tail; + + +/** + * Force all pay contexts to be resumed as we are about + * to shut down MHD. + */ +void +MH_force_pc_resume () +{ + for (struct PayContext *pc = pc_head; NULL != pc; pc = pc->next) + { + if (GNUNET_YES == pc->suspended) + { + pc->suspended = GNUNET_SYSERR; + MHD_resume_connection (pc->connection); + } + } +} /** @@ -309,6 +355,8 @@ resume_pay_with_response (struct PayContext *pc, GNUNET_SCHEDULER_cancel (pc->timeout_task); pc->timeout_task = NULL; } + GNUNET_assert (GNUNET_YES == pc->suspended); + pc->suspended = GNUNET_NO; MHD_resume_connection (pc->connection); TMH_trigger_daemon (); /* we resumed, kick MHD */ } @@ -479,16 +527,14 @@ static void pay_context_cleanup (struct TM_HandlerContext *hc) { struct PayContext *pc = (struct PayContext *) hc; - unsigned int i; if (NULL != pc->timeout_task) { GNUNET_SCHEDULER_cancel (pc->timeout_task); pc->timeout_task = NULL; } - TMH_PARSE_post_cleanup_callback (pc->json_parse_context); - for (i=0;icoins_cnt;i++) + for (unsigned int i=0;icoins_cnt;i++) { struct DepositConfirmation *dc = &pc->dc[i]; @@ -529,6 +575,9 @@ pay_context_cleanup (struct TM_HandlerContext *hc) json_decref (pc->proposal_data); pc->proposal_data = NULL; } + GNUNET_CONTAINER_DLL_remove (pc_head, + pc_tail, + pc); GNUNET_free (pc); } @@ -1262,12 +1311,11 @@ handler_pay_json (struct MHD_Connection *connection, { struct MHD_Response *resp; int ret; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Payment succeeded in the past; take short cut" - " and accept immediately.\n"); /* Payment succeeded in the past; take short cut and accept immediately */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Payment succeeded in the past; taking short cut"); resp = MHD_create_response_from_buffer (0, NULL, MHD_RESPMEM_PERSISTENT); @@ -1300,6 +1348,7 @@ handler_pay_json (struct MHD_Connection *connection, if (GNUNET_NO == pc->transaction_exists) { struct GNUNET_TIME_Absolute now; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Dealing with new transaction '%s'\n", GNUNET_h2s (&pc->h_proposal_data)); @@ -1309,12 +1358,11 @@ handler_pay_json (struct MHD_Connection *connection, { /* Time expired, we don't accept this payment now! */ const char *pd_str; - pd_str = GNUNET_STRINGS_absolute_time_to_string (pc->pay_deadline); + pd_str = GNUNET_STRINGS_absolute_time_to_string (pc->pay_deadline); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Attempt to get coins for expired contract. Deadline: '%s'\n", pd_str); - return TMH_RESPONSE_reply_bad_request (connection, TALER_EC_PAY_OFFER_EXPIRED, "The time to pay for this contract has expired."); @@ -1341,6 +1389,7 @@ handler_pay_json (struct MHD_Connection *connection, } MHD_suspend_connection (connection); + pc->suspended = GNUNET_YES; /* Find the responsible exchange, this may take a while... */ pc->fo = TMH_EXCHANGES_find_exchange (pc->chosen_exchange, @@ -1390,6 +1439,9 @@ MH_handler_pay (struct TMH_RequestHandler *rh, if (NULL == *connection_cls) { pc = GNUNET_new (struct PayContext); + GNUNET_CONTAINER_DLL_insert (pc_head, + pc_tail, + pc); pc->hc.cc = &pay_context_cleanup; pc->connection = connection; *connection_cls = pc; diff --git a/src/backend/taler-merchant-httpd_pay.h b/src/backend/taler-merchant-httpd_pay.h index 124a9d97..d4f4958a 100644 --- a/src/backend/taler-merchant-httpd_pay.h +++ b/src/backend/taler-merchant-httpd_pay.h @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2014, 2015 GNUnet e.V. + (C) 2014-2017 GNUnet e.V. TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -23,6 +23,15 @@ #include #include "taler-merchant-httpd.h" + +/** + * Force all pay contexts to be resumed as we are about + * to shut down MHD. + */ +void +MH_force_pc_resume (void); + + /** * Manage a payment * -- cgit v1.2.3