diff options
Diffstat (limited to 'src/bank-lib/bank_api_credit.c')
-rw-r--r-- | src/bank-lib/bank_api_credit.c | 233 |
1 files changed, 129 insertions, 104 deletions
diff --git a/src/bank-lib/bank_api_credit.c b/src/bank-lib/bank_api_credit.c index 33f66b48f..124415b80 100644 --- a/src/bank-lib/bank_api_credit.c +++ b/src/bank-lib/bank_api_credit.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2017--2020 Taler Systems SA + Copyright (C) 2017--2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -30,6 +30,13 @@ /** + * How much longer than the application-specified timeout + * do we wait (giving the server a chance to respond)? + */ +#define GRACE_PERIOD_MS 1000 + + +/** * @brief A /history/incoming Handle */ struct TALER_BANK_CreditHistoryHandle @@ -66,66 +73,71 @@ struct TALER_BANK_CreditHistoryHandle * were set, * #GNUNET_SYSERR if there was a protocol violation in @a history */ -static int +static enum GNUNET_GenericReturnValue parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh, const json_t *history) { - json_t *history_array; + struct TALER_BANK_CreditHistoryResponse chr = { + .http_status = MHD_HTTP_OK, + .ec = TALER_EC_NONE, + .response = history + }; + const json_t *history_array; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("incoming_transactions", + &history_array), + GNUNET_JSON_spec_string ("credit_account", + &chr.details.ok.credit_account_uri), + GNUNET_JSON_spec_end () + }; - if (NULL == (history_array = json_object_get (history, - "incoming_transactions"))) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (! json_is_array (history_array)) + if (GNUNET_OK != + GNUNET_JSON_parse (history, + spec, + NULL, + NULL)) { GNUNET_break_op (0); return GNUNET_SYSERR; } - for (unsigned int i = 0; i<json_array_size (history_array); i++) { - struct TALER_BANK_CreditDetails td; - uint64_t row_id; - struct GNUNET_JSON_Specification hist_spec[] = { - TALER_JSON_spec_amount ("amount", - &td.amount), - GNUNET_JSON_spec_absolute_time ("date", - &td.execution_date), - GNUNET_JSON_spec_uint64 ("row_id", - &row_id), - GNUNET_JSON_spec_fixed_auto ("reserve_pub", - &td.reserve_pub), - GNUNET_JSON_spec_string ("debit_account", - &td.debit_account_url), - GNUNET_JSON_spec_string ("credit_account", - &td.credit_account_url), - GNUNET_JSON_spec_end () - }; - json_t *transaction = json_array_get (history_array, - i); + size_t len = json_array_size (history_array); + struct TALER_BANK_CreditDetails cd[GNUNET_NZL (len)]; - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - hist_spec, - NULL, NULL)) + GNUNET_break_op (0 != len); + for (size_t i = 0; i<len; i++) { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - hh->hcb (hh->hcb_cls, - MHD_HTTP_OK, - TALER_EC_NONE, - row_id, - &td, - transaction)) - { - hh->hcb = NULL; - GNUNET_JSON_parse_free (hist_spec); - return GNUNET_OK; + struct TALER_BANK_CreditDetails *td = &cd[i]; + struct GNUNET_JSON_Specification hist_spec[] = { + TALER_JSON_spec_amount_any ("amount", + &td->amount), + GNUNET_JSON_spec_timestamp ("date", + &td->execution_date), + GNUNET_JSON_spec_uint64 ("row_id", + &td->serial_id), + GNUNET_JSON_spec_fixed_auto ("reserve_pub", + &td->reserve_pub), + GNUNET_JSON_spec_string ("debit_account", + &td->debit_account_uri), + GNUNET_JSON_spec_end () + }; + json_t *transaction = json_array_get (history_array, + i); + + if (GNUNET_OK != + GNUNET_JSON_parse (transaction, + hist_spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } } - GNUNET_JSON_parse_free (hist_spec); + chr.details.ok.details_length = len; + chr.details.ok.details = cd; + hh->hcb (hh->hcb_cls, + &chr); } return GNUNET_OK; } @@ -145,100 +157,84 @@ handle_credit_history_finished (void *cls, const void *response) { struct TALER_BANK_CreditHistoryHandle *hh = cls; - const json_t *j = response; - enum TALER_ErrorCode ec; + struct TALER_BANK_CreditHistoryResponse chr = { + .http_status = response_code, + .response = response + }; hh->job = NULL; switch (response_code) { case 0: - ec = TALER_EC_INVALID_RESPONSE; + chr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; case MHD_HTTP_OK: if (GNUNET_OK != parse_account_history (hh, - j)) + chr.response)) { GNUNET_break_op (0); - response_code = 0; - ec = TALER_EC_INVALID_RESPONSE; + json_dumpf (chr.response, + stderr, + JSON_INDENT (2)); + chr.http_status = 0; + chr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } - response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */ - ec = TALER_EC_NONE; + TALER_BANK_credit_history_cancel (hh); + return; + case MHD_HTTP_NO_CONTENT: break; case MHD_HTTP_BAD_REQUEST: /* This should never happen, either us or the bank is buggy (or API version conflict); just pass JSON reply to the application */ GNUNET_break_op (0); - ec = TALER_JSON_get_error_code (j); + chr.ec = TALER_JSON_get_error_code (chr.response); break; case MHD_HTTP_UNAUTHORIZED: /* Nothing really to verify, bank says the HTTP Authentication failed. May happen if HTTP authentication is used and the user supplied a wrong username/password combination. */ - ec = TALER_JSON_get_error_code (j); + chr.ec = TALER_JSON_get_error_code (chr.response); break; case MHD_HTTP_NOT_FOUND: /* Nothing really to verify: the bank is either unaware of the endpoint (not a bank), or of the account. We should pass the JSON (?) reply to the application */ - ec = TALER_JSON_get_error_code (j); + chr.ec = TALER_JSON_get_error_code (chr.response); break; case MHD_HTTP_INTERNAL_SERVER_ERROR: /* Server had an internal issue; we should retry, but this API leaves this to the application */ - ec = TALER_JSON_get_error_code (j); + chr.ec = TALER_JSON_get_error_code (chr.response); break; default: /* unexpected response code */ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u\n", (unsigned int) response_code); - GNUNET_break_op (0); - ec = TALER_JSON_get_error_code (j); + chr.ec = TALER_JSON_get_error_code (chr.response); break; } - if (NULL != hh->hcb) - hh->hcb (hh->hcb_cls, - response_code, - ec, - 0LLU, - NULL, - j); + hh->hcb (hh->hcb_cls, + &chr); TALER_BANK_credit_history_cancel (hh); } -/** - * Request the credit history of the exchange's bank account. - * - * @param ctx curl context for the event loop - * @param auth authentication data to use - * @param start_row from which row on do we want to get results, - * use UINT64_MAX for the latest; exclusive - * @param num_results how many results do we want; - * negative numbers to go into the past, positive numbers - * to go into the future starting at @a start_row; - * must not be zero. - * @param hres_cb the callback to call with the transaction - * history - * @param hres_cb_cls closure for the above callback - * @return NULL if the inputs are invalid (i.e. zero value for - * @e num_results). In this case, the callback is not - * called. - */ struct TALER_BANK_CreditHistoryHandle * TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx, const struct TALER_BANK_AuthenticationData *auth, uint64_t start_row, int64_t num_results, + struct GNUNET_TIME_Relative timeout, TALER_BANK_CreditHistoryCallback hres_cb, void *hres_cb_cls) { char url[128]; struct TALER_BANK_CreditHistoryHandle *hh; CURL *eh; + unsigned long long tms; if (0 == num_results) { @@ -246,20 +242,49 @@ TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx, return NULL; } + tms = (unsigned long long) (timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); if ( ( (UINT64_MAX == start_row) && (0 > num_results) ) || ( (0 == start_row) && (0 < num_results) ) ) - GNUNET_snprintf (url, - sizeof (url), - "history/incoming?delta=%lld", - (long long) num_results); + { + if ( (0 < num_results) && + (! GNUNET_TIME_relative_is_zero (timeout)) ) + /* 0 == start_row is implied, go with timeout into future */ + GNUNET_snprintf (url, + sizeof (url), + "history/incoming?delta=%lld&long_poll_ms=%llu", + (long long) num_results, + tms); + else + /* Going back from current transaction or have no timeout; + hence timeout makes no sense */ + GNUNET_snprintf (url, + sizeof (url), + "history/incoming?delta=%lld", + (long long) num_results); + } else - GNUNET_snprintf (url, - sizeof (url), - "history/incoming?delta=%lld&start=%llu", - (long long) num_results, - (unsigned long long) start_row); + { + if ( (0 < num_results) && + (! GNUNET_TIME_relative_is_zero (timeout)) ) + /* going forward from num_result */ + GNUNET_snprintf (url, + sizeof (url), + "history/incoming?delta=%lld&start=%llu&long_poll_ms=%llu", + (long long) num_results, + (unsigned long long) start_row, + tms); + else + /* going backwards or have no timeout; + hence timeout makes no sense */ + GNUNET_snprintf (url, + sizeof (url), + "history/incoming?delta=%lld&start=%llu", + (long long) num_results, + (unsigned long long) start_row); + } hh = GNUNET_new (struct TALER_BANK_CreditHistoryHandle); hh->hcb = hres_cb; hh->hcb_cls = hres_cb_cls; @@ -291,6 +316,13 @@ TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx, curl_easy_cleanup (eh); return NULL; } + if (0 != tms) + { + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TIMEOUT_MS, + (long) tms + GRACE_PERIOD_MS)); + } hh->job = GNUNET_CURL_job_add2 (ctx, eh, NULL, @@ -300,13 +332,6 @@ TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx, } -/** - * Cancel a history request. This function cannot be - * used on a request handle if a response is already - * served for it. - * - * @param hh the history request handle - */ void TALER_BANK_credit_history_cancel (struct TALER_BANK_CreditHistoryHandle *hh) { |