summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2019-11-09 13:57:07 +0100
committerChristian Grothoff <christian@grothoff.org>2019-11-09 13:57:07 +0100
commit7ceb24c57d843a5ba7aac4b6cfe5e2064dd16758 (patch)
treef2129415302f0f42b21c430f5687d6a6dc692b1d /src
parent8629eb50c25e71cd58de6932c6129998ec4cb5ac (diff)
downloadmerchant-7ceb24c57d843a5ba7aac4b6cfe5e2064dd16758.tar.gz
merchant-7ceb24c57d843a5ba7aac4b6cfe5e2064dd16758.tar.bz2
merchant-7ceb24c57d843a5ba7aac4b6cfe5e2064dd16758.zip
implement long polling for /check-payment
Diffstat (limited to 'src')
-rw-r--r--src/backend/taler-merchant-httpd.c67
-rw-r--r--src/backend/taler-merchant-httpd.h11
-rw-r--r--src/backend/taler-merchant-httpd_check-payment.c65
-rw-r--r--src/backend/taler-merchant-httpd_poll-payment.c63
-rw-r--r--src/lib/testing_api_cmd_check_payment.c18
-rw-r--r--src/lib/testing_api_cmd_poll_payment.c24
6 files changed, 164 insertions, 84 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 5daea7b6..324fb73b 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -283,6 +283,73 @@ TMH_compute_pay_key (const char *order_id,
/**
+ * Resume processing all suspended connections past timeout.
+ *
+ * @param cls unused
+ */
+static void
+do_resume (void *cls)
+{
+ struct TMH_SuspendedConnection *sc;
+
+ (void) cls;
+ resume_timeout_task = NULL;
+ while (1)
+ {
+ sc = GNUNET_CONTAINER_heap_peek (resume_timeout_heap);
+ if (NULL == sc)
+ return;
+ if (0 !=
+ GNUNET_TIME_absolute_get_remaining (
+ sc->long_poll_timeout).rel_value_us)
+ break;
+ GNUNET_assert (sc ==
+ GNUNET_CONTAINER_heap_remove_root (resume_timeout_heap));
+ sc->hn = NULL;
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
+ &sc->key,
+ sc));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Resuming long polled job due to timeout\n");
+ MHD_resume_connection (sc->con);
+ }
+ resume_timeout_task = GNUNET_SCHEDULER_add_at (sc->long_poll_timeout,
+ &do_resume,
+ NULL);
+}
+
+
+/**
+ * Suspend connection from @a sc until payment has been received.
+ *
+ * @param sc connection to suspend
+ */
+void
+TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc)
+{
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (payment_trigger_map,
+ &sc->key,
+ sc,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+ sc->hn = GNUNET_CONTAINER_heap_insert (resume_timeout_heap,
+ sc,
+ sc->long_poll_timeout.abs_value_us);
+ MHD_suspend_connection (sc->con);
+ if (NULL != resume_timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (resume_timeout_task);
+ resume_timeout_task = NULL;
+ }
+ sc = GNUNET_CONTAINER_heap_peek (resume_timeout_heap);
+ resume_timeout_task = GNUNET_SCHEDULER_add_at (sc->long_poll_timeout,
+ &do_resume,
+ NULL);
+}
+
+
+/**
* Create a taler://pay/ URI for the given @a con and @a order_id
* and @a session_id and @a instance_id.
*
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
index 53dedcb4..03999aab 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -394,6 +394,7 @@ extern struct GNUNET_TIME_Relative default_pay_deadline;
void
TMH_trigger_daemon (void);
+
/**
* Compute @a key to use for @a order_id and @a mpub in our
* #payment_trigger_map.
@@ -409,6 +410,15 @@ TMH_compute_pay_key (const char *order_id,
/**
+ * Suspend connection from @a sc until payment has been received.
+ *
+ * @param sc connection to suspend
+ */
+void
+TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc);
+
+
+/**
* Create a taler://pay/ URI for the given @a con and @a order_id
* and @a session_id and @a instance_id.
*
@@ -424,4 +434,5 @@ TMH_make_taler_pay_uri (struct MHD_Connection *con,
const char *session_id,
const char *instance_id);
+
#endif
diff --git a/src/backend/taler-merchant-httpd_check-payment.c b/src/backend/taler-merchant-httpd_check-payment.c
index fad3fdc6..d2356430 100644
--- a/src/backend/taler-merchant-httpd_check-payment.c
+++ b/src/backend/taler-merchant-httpd_check-payment.c
@@ -48,7 +48,11 @@ struct CheckPaymentRequestContext
*/
struct TM_HandlerContext hc;
- struct MHD_Connection *connection;
+ /**
+ * Entry in the #resume_timeout_heap for this check payment, if we are
+ * suspended.
+ */
+ struct TMH_SuspendedConnection sc;
/**
* Which merchant instance is this for?
@@ -173,11 +177,25 @@ process_refunds_cb (void *cls,
* @return #MHD_YES on success
*/
static int
-send_pay_request (const struct CheckPaymentRequestContext *cprc)
+send_pay_request (struct CheckPaymentRequestContext *cprc)
{
int ret;
char *already_paid_order_id = NULL;
char *taler_pay_uri;
+ struct GNUNET_TIME_Relative remaining;
+
+ remaining = GNUNET_TIME_absolute_get_remaining (cprc->sc.long_poll_timeout);
+ if (0 != remaining.rel_value_us)
+ {
+ /* long polling: do not queue a response, suspend connection instead */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Suspending /check-payment\n");
+ TMH_compute_pay_key (cprc->order_id,
+ &cprc->mi->pubkey,
+ &cprc->sc.key);
+ TMH_long_poll_suspend (&cprc->sc);
+ return MHD_YES;
+ }
/* Check if resource_id has been paid for in the same session
* with another order_id.
@@ -199,16 +217,16 @@ send_pay_request (const struct CheckPaymentRequestContext *cprc)
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
/* Always report on hard error as well to enable diagnostics */
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
- return TMH_RESPONSE_reply_internal_error (cprc->connection,
+ return TMH_RESPONSE_reply_internal_error (cprc->sc.con,
TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
"db error fetching pay session info");
}
}
- taler_pay_uri = TMH_make_taler_pay_uri (cprc->connection,
+ taler_pay_uri = TMH_make_taler_pay_uri (cprc->sc.con,
cprc->order_id,
cprc->session_id,
cprc->mi->id);
- ret = TMH_RESPONSE_reply_json_pack (cprc->connection,
+ ret = TMH_RESPONSE_reply_json_pack (cprc->sc.con,
MHD_HTTP_OK,
"{s:s, s:s, s:b, s:s?}",
"taler_pay_uri", taler_pay_uri,
@@ -249,7 +267,7 @@ parse_contract_terms (struct CheckPaymentRequestContext *cprc)
{
GNUNET_break (0);
cprc->ret
- = TMH_RESPONSE_reply_internal_error (cprc->connection,
+ = TMH_RESPONSE_reply_internal_error (cprc->sc.con,
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
"Merchant database error (contract terms corrupted)");
return GNUNET_SYSERR;
@@ -260,7 +278,7 @@ parse_contract_terms (struct CheckPaymentRequestContext *cprc)
{
GNUNET_break (0);
cprc->ret
- = TMH_RESPONSE_reply_internal_error (cprc->connection,
+ = TMH_RESPONSE_reply_internal_error (cprc->sc.con,
TALER_EC_CHECK_PAYMENT_FAILED_COMPUTE_PROPOSAL_HASH,
"Failed to hash proposal");
return GNUNET_SYSERR;
@@ -299,13 +317,13 @@ check_order_and_request_payment (struct CheckPaymentRequestContext *cprc)
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
/* Always report on hard error as well to enable diagnostics */
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
- return TMH_RESPONSE_reply_internal_error (cprc->connection,
+ return TMH_RESPONSE_reply_internal_error (cprc->sc.con,
TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
"db error fetching order");
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
- return TMH_RESPONSE_reply_not_found (cprc->connection,
+ return TMH_RESPONSE_reply_not_found (cprc->sc.con,
TALER_EC_CHECK_PAYMENT_ORDER_ID_UNKNOWN,
"unknown order_id");
}
@@ -345,11 +363,13 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh,
if (NULL == cprc)
{
+ const char *long_poll_timeout_s;
+
/* First time here, parse request and check order is known */
cprc = GNUNET_new (struct CheckPaymentRequestContext);
cprc->hc.cc = &cprc_cleanup;
cprc->ret = GNUNET_SYSERR;
- cprc->connection = connection;
+ cprc->sc.con = connection;
cprc->mi = mi;
*connection_cls = cprc;
@@ -384,6 +404,31 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh,
cprc->session_id = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"session_id");
+ long_poll_timeout_s = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "timeout");
+ if (NULL != long_poll_timeout_s)
+ {
+ unsigned int timeout;
+
+ if (1 != sscanf (long_poll_timeout_s,
+ "%u",
+ &timeout))
+ {
+ GNUNET_break_op (0);
+ return TMH_RESPONSE_reply_bad_request (connection,
+ TALER_EC_PARAMETER_MALFORMED,
+ "timeout must be non-negative number");
+ }
+ cprc->sc.long_poll_timeout
+ = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (
+ GNUNET_TIME_UNIT_SECONDS,
+ timeout));
+ }
+ else
+ {
+ cprc->sc.long_poll_timeout = GNUNET_TIME_UNIT_ZERO_ABS;
+ }
db->preflight (db->cls);
qs = db->find_contract_terms (db->cls,
&cprc->contract_terms,
diff --git a/src/backend/taler-merchant-httpd_poll-payment.c b/src/backend/taler-merchant-httpd_poll-payment.c
index 3050267f..165a0b58 100644
--- a/src/backend/taler-merchant-httpd_poll-payment.c
+++ b/src/backend/taler-merchant-httpd_poll-payment.c
@@ -171,42 +171,6 @@ process_refunds_cb (void *cls,
/**
- * Resume processing all suspended connections past timeout.
- *
- * @param cls unused
- */
-static void
-do_resume (void *cls)
-{
- struct TMH_SuspendedConnection *sc;
-
- (void) cls;
- resume_timeout_task = NULL;
- while (1)
- {
- sc = GNUNET_CONTAINER_heap_peek (resume_timeout_heap);
- if (NULL == sc)
- return;
- if (0 !=
- GNUNET_TIME_absolute_get_remaining (
- sc->long_poll_timeout).rel_value_us)
- break;
- GNUNET_assert (sc ==
- GNUNET_CONTAINER_heap_remove_root (resume_timeout_heap));
- sc->hn = NULL;
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
- &sc->key,
- sc));
- MHD_resume_connection (sc->con);
- }
- resume_timeout_task = GNUNET_SCHEDULER_add_at (sc->long_poll_timeout,
- &do_resume,
- NULL);
-}
-
-
-/**
* The client did not yet pay, send it the payment request.
*
* @param pprc check pay request context
@@ -223,31 +187,13 @@ send_pay_request (struct PollPaymentRequestContext *pprc)
remaining = GNUNET_TIME_absolute_get_remaining (pprc->sc.long_poll_timeout);
if (0 != remaining.rel_value_us)
{
- struct TMH_SuspendedConnection *sc;
-
/* long polling: do not queue a response, suspend connection instead */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Suspending /poll-payment\n");
TMH_compute_pay_key (pprc->order_id,
&pprc->mi->pubkey,
&pprc->sc.key);
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (payment_trigger_map,
- &pprc->sc.key,
- &pprc->sc,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
- pprc->sc.hn = GNUNET_CONTAINER_heap_insert (resume_timeout_heap,
- &pprc->sc,
- pprc->sc.long_poll_timeout.
- abs_value_us);
- MHD_suspend_connection (pprc->sc.con);
- if (NULL != resume_timeout_task)
- {
- GNUNET_SCHEDULER_cancel (resume_timeout_task);
- resume_timeout_task = NULL;
- }
- sc = GNUNET_CONTAINER_heap_peek (resume_timeout_heap);
- resume_timeout_task = GNUNET_SCHEDULER_add_at (sc->long_poll_timeout,
- &do_resume,
- NULL);
+ TMH_long_poll_suspend (&pprc->sc);
return MHD_YES;
}
@@ -276,6 +222,8 @@ send_pay_request (struct PollPaymentRequestContext *pprc)
"db error fetching pay session info");
}
}
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Sending payment request in /poll-payment\n");
taler_pay_uri = TMH_make_taler_pay_uri (pprc->sc.con,
pprc->order_id,
pprc->session_id,
@@ -451,7 +399,6 @@ MH_handler_poll_payment (struct TMH_RequestHandler *rh,
"Merchant database error (contract terms corrupted)");
}
}
-
} /* end of first-time initialization / sanity checks */
diff --git a/src/lib/testing_api_cmd_check_payment.c b/src/lib/testing_api_cmd_check_payment.c
index a942c587..2e988d75 100644
--- a/src/lib/testing_api_cmd_check_payment.c
+++ b/src/lib/testing_api_cmd_check_payment.c
@@ -122,23 +122,15 @@ check_payment_cb (void *cls,
cps->cpo = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "check payment: expected paid: %s: %d\n",
- TALER_TESTING_interpreter_get_current_label (
- cps->is),
- cps->expect_paid);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "check payment: paid: %d\n",
- paid);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "check payment: url: %s\n",
+ "check payment (%s): expected paid: %d, paid: %d, url: %s\n",
+ TALER_TESTING_interpreter_get_current_label (cps->is),
+ cps->expect_paid,
+ paid,
taler_pay_uri);
-
if (paid != cps->expect_paid)
TALER_TESTING_FAIL (cps->is);
-
if (cps->http_status != http_status)
TALER_TESTING_FAIL (cps->is);
-
TALER_TESTING_interpreter_next (cps->is);
}
@@ -175,7 +167,7 @@ check_payment_run (void *cls,
order_id,
NULL,
GNUNET_TIME_UNIT_ZERO,
- check_payment_cb,
+ &check_payment_cb,
cps);
GNUNET_assert (NULL != cps->cpo);
}
diff --git a/src/lib/testing_api_cmd_poll_payment.c b/src/lib/testing_api_cmd_poll_payment.c
index 9cef2211..c6200fb8 100644
--- a/src/lib/testing_api_cmd_poll_payment.c
+++ b/src/lib/testing_api_cmd_poll_payment.c
@@ -181,6 +181,12 @@ conclude_task (void *cls)
TALER_TESTING_interpreter_lookup_command (ppc->is,
ppc->start_reference);
cps = poll_cmd->cls;
+ if (NULL != cps->cpo)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected /poll/payment to have completed, but it did not!\n");
+ TALER_TESTING_FAIL (ppc->is);
+ }
if (cps->http_status != ppc->expected_http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -238,7 +244,9 @@ poll_payment_cb (void *cls,
{
struct PollPaymentStartState *cps = cls;
- if (MHD_HTTP_OK != http_status)
+ cps->cpo = NULL;
+ if ( (MHD_HTTP_OK != http_status) &&
+ (NULL != obj) )
{
char *log = json_dumps (obj,
JSON_COMPACT);
@@ -249,7 +257,14 @@ poll_payment_cb (void *cls,
log);
free (log);
}
- cps->cpo = NULL;
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Poll payment returned %u (%d/%d)\n",
+ http_status,
+ paid,
+ refunded);
+ }
cps->paid = paid;
cps->http_status = http_status;
cps->refunded = refunded;
@@ -301,7 +316,10 @@ poll_payment_start_run (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Polling for order id `%s'\n",
order_id);
- cps->deadline = GNUNET_TIME_relative_to_absolute (cps->timeout);
+ /* add 1s grace time to timeout */
+ cps->deadline
+ = GNUNET_TIME_absolute_add (GNUNET_TIME_relative_to_absolute (cps->timeout),
+ GNUNET_TIME_UNIT_SECONDS);
cps->cpo = TALER_MERCHANT_poll_payment (is->ctx,
cps->merchant_url,
order_id,