diff options
author | Marcello Stanisci <marcello.stanisci@inria.fr> | 2016-05-25 00:13:59 +0200 |
---|---|---|
committer | Marcello Stanisci <marcello.stanisci@inria.fr> | 2016-05-25 00:13:59 +0200 |
commit | 5c2403c8aed2cb34efdb7fe25b62e80f27d986d8 (patch) | |
tree | 3782cca1b892d3909aa8a4be6e79c341e9c2e78d /src/backend | |
parent | a1ecc4a85e1634a0c02ac84f140195040138a254 (diff) | |
parent | 8d7fe0a745d51d85eb46c5fdb879651ad13816de (diff) |
Merge branch 'master' of git://taler.net/merchant
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/taler-merchant-httpd_auditors.c | 3 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_contract.c | 35 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_exchanges.c | 120 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_parsing.c | 10 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_pay.c | 79 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_util.c | 3 |
6 files changed, 133 insertions, 117 deletions
diff --git a/src/backend/taler-merchant-httpd_auditors.c b/src/backend/taler-merchant-httpd_auditors.c index 0b583fd4..4b4bd646 100644 --- a/src/backend/taler-merchant-httpd_auditors.c +++ b/src/backend/taler-merchant-httpd_auditors.c @@ -210,8 +210,7 @@ TMH_AUDITORS_init (const struct GNUNET_CONFIGURATION_Handle *cfg) json_array_append_new (j_auditors, json_pack ("{s:s, s:o, s:s}", "name", auditors[cnt].name, - "auditor_pub", GNUNET_JSON_from_data (&auditors[cnt].public_key, - sizeof (struct TALER_AuditorPublicKeyP)), + "auditor_pub", GNUNET_JSON_from_data_auto (&auditors[cnt].public_key), "uri", auditors[cnt].uri)); return nauditors; } diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index 9d159583..1f55ada9 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -187,29 +187,6 @@ MH_handler_contract (struct TMH_RequestHandler *rh, "products in contract request malformed"); } - /* Check if this transaction ID erroneously corresponds to a - contract that already paid, in which case we should refuse - to sign it again (frontend buggy, it should use a fresh - transaction ID each time)! */ - if (GNUNET_OK == - db->check_payment (db->cls, - transaction_id)) - { - struct MHD_Response *resp; - int ret; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Transaction %llu already paid in the past, refusing to sign!\n", - (unsigned long long) transaction_id); - resp = MHD_create_response_from_buffer (strlen ("Duplicate transaction ID!"), - "Duplicate transaction ID!", - MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response (connection, - MHD_HTTP_FORBIDDEN, - resp); - MHD_destroy_response (resp); - return ret; - } /* add fields to the contract that the backend should provide */ json_object_set (jcontract, @@ -220,12 +197,10 @@ MH_handler_contract (struct TMH_RequestHandler *rh, j_auditors); json_object_set_new (jcontract, "H_wire", - GNUNET_JSON_from_data (&h_wire, - sizeof (h_wire))); + GNUNET_JSON_from_data_auto (&h_wire)); json_object_set_new (jcontract, "merchant_pub", - GNUNET_JSON_from_data (&pubkey, - sizeof (pubkey))); + GNUNET_JSON_from_data_auto (&pubkey)); /* create contract signature */ contract.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); @@ -247,10 +222,8 @@ MH_handler_contract (struct TMH_RequestHandler *rh, MHD_HTTP_OK, "{s:O, s:O, s:O}", "contract", jcontract, - "merchant_sig", GNUNET_JSON_from_data (&contract_sig, - sizeof (contract_sig)), - "H_contract", GNUNET_JSON_from_data (&contract.h_contract, - sizeof (contract.h_contract))); + "merchant_sig", GNUNET_JSON_from_data_auto (&contract_sig), + "H_contract", GNUNET_JSON_from_data_auto (&contract.h_contract)); GNUNET_JSON_parse_free (spec); json_decref (root); return res; diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c index 9a292365..d980ef09 100644 --- a/src/backend/taler-merchant-httpd_exchanges.c +++ b/src/backend/taler-merchant-httpd_exchanges.c @@ -24,10 +24,26 @@ #include "taler-merchant-httpd_exchanges.h" + +/** + * Delay after which we'll re-fetch key information from the exchange. + */ +#define RELOAD_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2) + +/** + * Threshold after which exponential backoff should not increase. + */ +#define RETRY_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) + + /** - * How often do we retry fetching /keys? + * Perform our exponential back-off calculation, starting at 1 ms + * and then going by a factor of 2 up unto a maximum of RETRY_BACKOFF_THRESHOLD. + * + * @param r current backoff time, initially zero */ -#define KEYS_RETRY_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) +#define RETRY_BACKOFF(r) GNUNET_TIME_relative_min (RETRY_BACKOFF_THRESHOLD, \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MILLISECONDS, (r)), 2)); /** @@ -68,7 +84,8 @@ struct TMH_EXCHANGES_FindOperation struct Exchange *my_exchange; /** - * Task scheduled to asynchrnously return the result. + * Task scheduled to asynchronously return the result to + * the find continuation. */ struct GNUNET_SCHEDULER_Task *at; @@ -118,18 +135,23 @@ struct Exchange struct TALER_MasterPublicKeyP master_pub; /** - * At what time should we try to fetch /keys again? + * How long should we wait between the next retry? */ - struct GNUNET_TIME_Absolute retry_time; + struct GNUNET_TIME_Relative retry_delay; /** * Task where we retry fetching /keys from the exchange. + * + * Can also be active when pending=GNUNET_NO, + * since we periodically (every hour) reload the + * exchange keys. */ struct GNUNET_SCHEDULER_Task *retry_task; /** - * Flag which indicates whether some HTTP transfer between - * this merchant and the exchange is still ongoing + * GNUNET_YES to indicate that there is an ongoing + * transfer we're waiting for, + * GNUNET_NO to indicate that key data is up-to-date. */ int pending; @@ -201,12 +223,14 @@ retry_exchange (void *cls) { struct Exchange *exchange = cls; + /* might be a scheduled reload and not our first attempt */ + exchange->pending = GNUNET_YES; exchange->retry_task = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to exchange exchange %s in retry_exchange\n", exchange->uri); - exchange->pending = GNUNET_SYSERR; /* failed hard */ exchange->conn = TALER_EXCHANGE_connect (merchant_curl_ctx, exchange->uri, &keys_mgmt_cb, @@ -237,38 +261,36 @@ keys_mgmt_cb (void *cls, struct Exchange *exchange = cls; struct TMH_EXCHANGES_FindOperation *fo; - if (NULL != keys) - { - exchange->pending = GNUNET_NO; - } - else + GNUNET_assert (GNUNET_YES == exchange->pending); + + if (NULL == keys) { + exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to fetch /keys from `%s'\n", - exchange->uri); + "Failed to fetch /keys from `%s', retrying in %s\n", + exchange->uri, + GNUNET_STRINGS_relative_time_to_string (exchange->retry_delay, GNUNET_YES)); TALER_EXCHANGE_disconnect (exchange->conn); exchange->conn = NULL; - exchange->retry_time = GNUNET_TIME_relative_to_absolute (KEYS_RETRY_FREQ); - /* Always retry trusted exchanges in the background, so that we don't have - * to wait for a customer to trigger it and thus delay his response */ - if (GNUNET_YES == exchange->trusted) - { - exchange->retry_task = GNUNET_SCHEDULER_add_delayed (KEYS_RETRY_FREQ, - &retry_exchange, - exchange); - } - else - { - exchange->pending = GNUNET_SYSERR; /* failed hard */ - } + exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay, + &retry_exchange, + exchange); + return; } + + exchange->pending = GNUNET_NO; + /* Schedule for our regular reload. */ + /* FIXME: we might want to take HTTP cache control into account */ + exchange->retry_task = GNUNET_SCHEDULER_add_delayed (RELOAD_DELAY, + &retry_exchange, + exchange); while (NULL != (fo = exchange->fo_head)) { GNUNET_CONTAINER_DLL_remove (exchange->fo_head, exchange->fo_tail, fo); fo->fc (fo->fc_cls, - (NULL != keys) ? exchange->conn : NULL, + exchange->conn, exchange->trusted); GNUNET_free (fo); } @@ -312,7 +334,7 @@ return_result (void *cls) */ struct TMH_EXCHANGES_FindOperation * TMH_EXCHANGES_find_exchange (const char *chosen_exchange, - TMH_EXCHANGES_FindContinuation fc, // process payment + TMH_EXCHANGES_FindContinuation fc, void *fc_cls) { struct Exchange *exchange; @@ -359,29 +381,6 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange, chosen_exchange); } - if (GNUNET_SYSERR == exchange->pending) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Maybe retrying previously contacted exchange `%s'\n", - chosen_exchange); - /* check if we should resume this exchange */ - if (0 == GNUNET_TIME_absolute_get_remaining (exchange->retry_time).rel_value_us) - { - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Retrying exchange `%s'\n", - chosen_exchange); - exchange->pending = GNUNET_YES; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Not retrying exchange `%s', too early\n", - chosen_exchange); - } - } - - fo = GNUNET_new (struct TMH_EXCHANGES_FindOperation); fo->fc = fc; fo->fc_cls = fc_cls; @@ -434,13 +433,14 @@ TMH_EXCHANGES_find_exchange_cancel (struct TMH_EXCHANGES_FindOperation *fo) /** * Function called on each configuration section. Finds sections - * about exchanges and parses the entries. + * about exchanges, parses the entries and tries to connect to + * it in order to fetch /keys. * * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *` * @param section name of the section */ static void -parse_exchanges (void *cls, +accept_exchanges (void *cls, const char *section) { const struct GNUNET_CONFIGURATION_Handle *cfg = cls; @@ -524,19 +524,19 @@ TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg) return GNUNET_SYSERR; } merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (merchant_curl_ctx); + /* get exchanges from the merchant configuration and try to connect to them */ GNUNET_CONFIGURATION_iterate_sections (cfg, - &parse_exchanges, + &accept_exchanges, (void *) cfg); - /* build JSON with list of trusted exchanges */ + /* build JSON with list of trusted exchanges (will be included in contracts) */ trusted_exchanges = json_array (); for (exchange = exchange_head; NULL != exchange; exchange = exchange->next) { if (GNUNET_YES != exchange->trusted) continue; j_exchange = json_pack ("{s:s, s:o}", - "url", exchange->uri, - "master_pub", GNUNET_JSON_from_data (&exchange->master_pub, - sizeof (struct TALER_MasterPublicKeyP))); + "url", exchange->uri, + "master_pub", GNUNET_JSON_from_data_auto (&exchange->master_pub)); json_array_append_new (trusted_exchanges, j_exchange); } diff --git a/src/backend/taler-merchant-httpd_parsing.c b/src/backend/taler-merchant-httpd_parsing.c index 18bccb5d..c7cdfd15 100644 --- a/src/backend/taler-merchant-httpd_parsing.c +++ b/src/backend/taler-merchant-httpd_parsing.c @@ -27,12 +27,6 @@ #include "taler-merchant-httpd_parsing.h" #include "taler-merchant-httpd_responses.h" -/* Although the following declaration isn't in any case useful -to a merchant's activity, it's needed here to make the function -'TMH_PARSE_nagivate_json ()' compile fine; so its value will be -kept on some merchant's accepted currency. For multi currencies -merchants, that of course would require a patch */ -extern char *TMH_merchant_currency_string; /** * Initial size for POST request buffer. @@ -129,9 +123,9 @@ buffer_append (struct Buffer *buf, if (data_size + buf->fill > buf->alloc) { char *new_buf; - size_t new_size = buf->alloc; + size_t new_size = buf->alloc ? buf->alloc : 1; while (new_size < buf->fill + data_size) - new_size += 2; + new_size *= 2; if (new_size > max_size) return GNUNET_NO; new_buf = GNUNET_malloc (new_size); diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index eb9fd506..89fe13c1 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -18,6 +18,7 @@ * @brief handling of /pay requests * @author Marcello Stanisci * @author Christian Grothoff + * @author Florian Dold */ #include "platform.h" #include <jansson.h> @@ -33,6 +34,12 @@ /** + * How long to wait before giving up processing with the exchange? + */ +#define PAY_TIMEOUT (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)) + + +/** * Information we keep for an individual call to the /pay handler. */ struct PayContext; @@ -206,6 +213,13 @@ 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; + }; @@ -228,13 +242,19 @@ resume_pay_with_response (struct PayContext *pc, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Resuming /pay handling as exchange interaction is done (%u)\n", response_code); + if (NULL != pc->timeout_task) + { + GNUNET_SCHEDULER_cancel (pc->timeout_task); + pc->timeout_task = NULL; + } MHD_resume_connection (pc->connection); TMH_trigger_daemon (); /* we resumed, kick MHD */ } + /** * Convert denomination key to its base32 representation - * + * * @param dk denomination key to convert * @return 0-terminated base32 encoding of @a dk, to be deallocated */ @@ -311,10 +331,13 @@ deposit_cb (void *cls, if (NULL == proof) { - /* FIXME: is this the right code for when the exchange fails? */ + /* We can't do anything meaningful here, the exchange did something wrong */ + /* FIXME: any useful information we can include? */ resume_pay_with_response (pc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TMH_RESPONSE_make_internal_error ("Exchange failed, no proof available")); + MHD_HTTP_SERVICE_UNAVAILABLE, + TMH_RESPONSE_make_json_pack ("{s:s, s:s}", + "error", "exchange failed", + "hint", "The exchange provided an unexpected response")); } else { @@ -325,8 +348,7 @@ deposit_cb (void *cls, eproof = json_copy ((json_t *) proof); json_object_set (eproof, "coin_pub", - GNUNET_JSON_from_data (&dc->coin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP))); + GNUNET_JSON_from_data_auto (&dc->coin_pub)); resume_pay_with_response (pc, http_status, TMH_RESPONSE_make_json (eproof)); @@ -376,6 +398,12 @@ 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;i<pc->coins_cnt;i++) { @@ -467,9 +495,10 @@ process_pay_with_exchange (void *cls, GNUNET_break_op (0); resume_pay_with_response (pc, MHD_HTTP_BAD_REQUEST, - TMH_RESPONSE_make_json_pack ("{s:s, s:o}", - "hint", "unknown denom to exchange", - "denom_pub", GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key))); + TMH_RESPONSE_make_json_pack ("{s:s, s:o, s:o}", + "error", "denomination not found", + "denom_pub", GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key), + "exchange_keys", TALER_EXCHANGE_get_keys_raw (mh))); denom_enc = denomination_to_string_alloc (&dc->denom); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "unknown denom to exchange: %s\n", denom_enc); GNUNET_free (denom_enc); @@ -485,10 +514,10 @@ process_pay_with_exchange (void *cls, resume_pay_with_response (pc, MHD_HTTP_BAD_REQUEST, TMH_RESPONSE_make_json_pack ("{s:s, s:o}", - "hint", "no acceptable auditor for denomination", + "error", "invalid denomination", "denom_pub", GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key))); denom_enc = denomination_to_string_alloc (&dc->denom); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "no acceptable auditor for denomination: %s\n", denom_enc); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "client offered invalid denomination: %s\n", denom_enc); GNUNET_free (denom_enc); return; } @@ -624,6 +653,30 @@ process_pay_with_exchange (void *cls, /** + * Handle a timeout for the processing of the pay request. + * + * @param cls closure + */ +static void +handle_pay_timeout (void *cls) +{ + struct PayContext *pc = cls; + + pc->timeout_task = NULL; + + if (NULL != pc->fo) + { + TMH_EXCHANGES_find_exchange_cancel (pc->fo); + pc->fo = NULL; + } + + resume_pay_with_response (pc, + MHD_HTTP_SERVICE_UNAVAILABLE, + TMH_RESPONSE_make_internal_error ("exchange not reachable")); +} + + +/** * Accomplish this payment. * * @param rh context of the handler @@ -834,9 +887,6 @@ MH_handler_pay (struct TMH_RequestHandler *rh, /* Payment succeeded in the past; take short cut and accept immediately */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Transaction %llu already paid in the past, taking short cut.\n", - (unsigned long long) pc->transaction_id); resp = MHD_create_response_from_buffer (0, NULL, MHD_RESPMEM_PERSISTENT); @@ -860,6 +910,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Suspending /pay handling while working with the exchange\n"); MHD_suspend_connection (connection); + GNUNET_SCHEDULER_add_delayed (PAY_TIMEOUT, handle_pay_timeout, pc); json_decref (root); return MHD_YES; } diff --git a/src/backend/taler-merchant-httpd_util.c b/src/backend/taler-merchant-httpd_util.c index 800e41ab..9d62722c 100644 --- a/src/backend/taler-merchant-httpd_util.c +++ b/src/backend/taler-merchant-httpd_util.c @@ -99,8 +99,7 @@ MH_handler_hash_contract (struct TMH_RequestHandler *rh, res = TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_OK, "{s:O}", - "hash", GNUNET_JSON_from_data (&hc, - sizeof (hc))); + "hash", GNUNET_JSON_from_data_auto (&hc)); json_decref (root); return res; } |