aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorMarcello Stanisci <marcello.stanisci@inria.fr>2016-05-25 00:13:59 +0200
committerMarcello Stanisci <marcello.stanisci@inria.fr>2016-05-25 00:13:59 +0200
commit5c2403c8aed2cb34efdb7fe25b62e80f27d986d8 (patch)
tree3782cca1b892d3909aa8a4be6e79c341e9c2e78d /src/backend
parenta1ecc4a85e1634a0c02ac84f140195040138a254 (diff)
parent8d7fe0a745d51d85eb46c5fdb879651ad13816de (diff)
Merge branch 'master' of git://taler.net/merchant
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/taler-merchant-httpd_auditors.c3
-rw-r--r--src/backend/taler-merchant-httpd_contract.c35
-rw-r--r--src/backend/taler-merchant-httpd_exchanges.c120
-rw-r--r--src/backend/taler-merchant-httpd_parsing.c10
-rw-r--r--src/backend/taler-merchant-httpd_pay.c79
-rw-r--r--src/backend/taler-merchant-httpd_util.c3
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;
}