aboutsummaryrefslogtreecommitdiff
path: root/src/bank-lib/fakebank.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bank-lib/fakebank.c')
-rw-r--r--src/bank-lib/fakebank.c716
1 files changed, 636 insertions, 80 deletions
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index ecb5934e6..580012b02 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -21,11 +21,12 @@
21 * @brief library that fakes being a Taler bank for testcases 21 * @brief library that fakes being a Taler bank for testcases
22 * @author Christian Grothoff <christian@grothoff.org> 22 * @author Christian Grothoff <christian@grothoff.org>
23 */ 23 */
24// TODO: support long polling
25// TODO: support adding WAD transfers 24// TODO: support adding WAD transfers
26 25
27#include "platform.h" 26#include "platform.h"
28#include <pthread.h> 27#include <pthread.h>
28#include <poll.h>
29#include <sys/eventfd.h>
29#include "taler_fakebank_lib.h" 30#include "taler_fakebank_lib.h"
30#include "taler_bank_service.h" 31#include "taler_bank_service.h"
31#include "taler_mhd_lib.h" 32#include "taler_mhd_lib.h"
@@ -44,6 +45,73 @@
44#define MAX_URL_LEN 64 45#define MAX_URL_LEN 64
45 46
46/** 47/**
48 * Per account information.
49 */
50struct Account;
51
52
53/**
54 * Types of long polling activities.
55 */
56enum LongPollType
57{
58 /**
59 * Transfer TO the exchange.
60 */
61 LP_CREDIT,
62
63 /**
64 * Transfer FROM the exchange.
65 */
66 LP_DEBIT
67
68};
69
70/**
71 * Client waiting for activity on this account.
72 */
73struct LongPoller
74{
75
76 /**
77 * Kept in a DLL.
78 */
79 struct LongPoller *next;
80
81 /**
82 * Kept in a DLL.
83 */
84 struct LongPoller *prev;
85
86 /**
87 * Account this long poller is waiting on.
88 */
89 struct Account *account;
90
91 /**
92 * Entry in the heap for this long poller.
93 */
94 struct GNUNET_CONTAINER_HeapNode *hn;
95
96 /**
97 * Client that is waiting for transactions.
98 */
99 struct MHD_Connection *conn;
100
101 /**
102 * When will this long poller time out?
103 */
104 struct GNUNET_TIME_Absolute timeout;
105
106 /**
107 * What does the @e connection wait for?
108 */
109 enum LongPollType type;
110
111};
112
113
114/**
47 * Details about a transcation we (as the simulated bank) received. 115 * Details about a transcation we (as the simulated bank) received.
48 */ 116 */
49struct Transaction; 117struct Transaction;
@@ -75,6 +143,16 @@ struct Account
75 struct Transaction *out_tail; 143 struct Transaction *out_tail;
76 144
77 /** 145 /**
146 * Kept in a DLL.
147 */
148 struct LongPoller *lp_head;
149
150 /**
151 * Kept in a DLL.
152 */
153 struct LongPoller *lp_tail;
154
155 /**
78 * Account name (string, not payto!) 156 * Account name (string, not payto!)
79 */ 157 */
80 char *account_name; 158 char *account_name;
@@ -257,6 +335,23 @@ struct TALER_FAKEBANK_Handle
257 struct GNUNET_SCHEDULER_Task *mhd_task; 335 struct GNUNET_SCHEDULER_Task *mhd_task;
258 336
259 /** 337 /**
338 * Task for expiring long-polling connections,
339 * unless we are using a thread pool (then NULL).
340 */
341 struct GNUNET_SCHEDULER_Task *lp_task;
342
343 /**
344 * Task for expiring long-polling connections, unless we are using the
345 * GNUnet scheduler (then NULL).
346 */
347 pthread_t lp_thread;
348
349 /**
350 * MIN-heap of long pollers, sorted by timeout.
351 */
352 struct GNUNET_CONTAINER_Heap *lp_heap;
353
354 /**
260 * Hashmap of reserve public keys to 355 * Hashmap of reserve public keys to
261 * `struct Transaction` with that reserve public 356 * `struct Transaction` with that reserve public
262 * key. Used to prevent public-key re-use. 357 * key. Used to prevent public-key re-use.
@@ -319,6 +414,17 @@ struct TALER_FAKEBANK_Handle
319 */ 414 */
320 uint16_t port; 415 uint16_t port;
321 416
417 /**
418 * Event FD to signal @a lp_thread a change in
419 * @a lp_heap.
420 */
421 int lp_event;
422
423 /**
424 * Set to true once we are shutting down.
425 */
426 bool in_shutdown;
427
322#if EPOLL_SUPPORT 428#if EPOLL_SUPPORT
323 /** 429 /**
324 * Boxed @e mhd_fd. 430 * Boxed @e mhd_fd.
@@ -334,6 +440,145 @@ struct TALER_FAKEBANK_Handle
334 440
335 441
336/** 442/**
443 * Special address "con_cls" can point to to indicate that the handler has
444 * been called more than once already (was previously suspended).
445 */
446static int special_ptr;
447
448
449/**
450 * Task run whenever HTTP server operations are pending.
451 *
452 * @param cls the `struct TALER_FAKEBANK_Handle`
453 */
454static void
455run_mhd (void *cls);
456
457
458/**
459 * Trigger the @a lp. Frees associated resources,
460 * except the entry of @a lp in the timeout heap.
461 * Must be called while the ``big lock`` is held.
462 *
463 * @param[in] lp long poller to trigger
464 * @param[in,out] h fakebank handle
465 */
466static void
467lp_trigger (struct LongPoller *lp,
468 struct TALER_FAKEBANK_Handle *h)
469{
470 struct Account *acc = lp->account;
471
472 GNUNET_CONTAINER_DLL_remove (acc->lp_head,
473 acc->lp_tail,
474 lp);
475 MHD_resume_connection (lp->conn);
476 GNUNET_free (lp);
477 if (NULL != h->mhd_task)
478 GNUNET_SCHEDULER_cancel (h->mhd_task);
479 h->mhd_task =
480 GNUNET_SCHEDULER_add_now (&run_mhd,
481 h);
482}
483
484
485/**
486 * Thread that is run to wake up connections that have hit their timeout. Runs
487 * until in_shutdown is set to true. Must be send signals via lp_event on
488 * shutdown and/or whenever the heap changes to an earlier timeout.
489 *
490 * @param cls a `struct TALER_FAKEBANK_Handle *`
491 * @return NULL
492 */
493static void *
494lp_expiration_thread (void *cls)
495{
496 struct TALER_FAKEBANK_Handle *h = cls;
497
498 GNUNET_assert (0 ==
499 pthread_mutex_lock (&h->big_lock));
500 while (! h->in_shutdown)
501 {
502 struct LongPoller *lp;
503 int timeout_ms;
504
505 lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
506 while ( (NULL != lp) &&
507 GNUNET_TIME_absolute_is_past (lp->timeout))
508 {
509 GNUNET_assert (lp ==
510 GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
511 GNUNET_assert (0 ==
512 pthread_mutex_lock (&h->big_lock));
513 lp_trigger (lp,
514 h);
515 GNUNET_assert (0 ==
516 pthread_mutex_unlock (&h->big_lock));
517 lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
518 }
519 if (NULL != lp)
520 {
521 struct GNUNET_TIME_Relative rem;
522 unsigned long long left_ms;
523
524 rem = GNUNET_TIME_absolute_get_remaining (lp->timeout);
525 left_ms = rem.rel_value_us / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
526 if (left_ms > INT_MAX)
527 timeout_ms = INT_MAX;
528 else
529 timeout_ms = (int) left_ms;
530 }
531 else
532 {
533 timeout_ms = -1; /* infinity */
534 }
535 GNUNET_assert (0 ==
536 pthread_mutex_unlock (&h->big_lock));
537 {
538 struct pollfd p = {
539 .fd = h->lp_event,
540 .events = POLLIN
541 };
542 int ret;
543
544 ret = poll (&p,
545 1,
546 timeout_ms);
547 if (-1 == ret)
548 {
549 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
550 "poll");
551 }
552 else if (1 == ret)
553 {
554 /* clear event */
555 uint64_t ev;
556 ssize_t iret;
557
558 iret = read (h->lp_event,
559 &ev,
560 sizeof (ev));
561 if (-1 == iret)
562 {
563 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
564 "read");
565 }
566 else
567 {
568 GNUNET_break (sizeof (uint64_t) == iret);
569 }
570 }
571 }
572 GNUNET_assert (0 ==
573 pthread_mutex_lock (&h->big_lock));
574 }
575 GNUNET_assert (0 ==
576 pthread_mutex_unlock (&h->big_lock));
577 return NULL;
578}
579
580
581/**
337 * Lookup account with @a name, and if it does not exist, create it. 582 * Lookup account with @a name, and if it does not exist, create it.
338 * 583 *
339 * @param[in,out] h bank to lookup account at 584 * @param[in,out] h bank to lookup account at
@@ -626,6 +871,36 @@ post_transaction (struct TALER_FAKEBANK_Handle *h,
626 ca->in_tail, 871 ca->in_tail,
627 old); 872 old);
628 } 873 }
874 {
875 struct LongPoller *nxt;
876
877 for (struct LongPoller *lp = debit_acc->lp_head;
878 NULL != lp;
879 lp = nxt)
880 {
881 nxt = lp->next;
882 if (LP_DEBIT == lp->type)
883 {
884 GNUNET_assert (lp ==
885 GNUNET_CONTAINER_heap_remove_node (lp->hn));
886 lp_trigger (lp,
887 h);
888 }
889 }
890 for (struct LongPoller *lp = credit_acc->lp_head;
891 NULL != lp;
892 lp = nxt)
893 {
894 nxt = lp->next;
895 if (LP_CREDIT == lp->type)
896 {
897 GNUNET_assert (lp ==
898 GNUNET_CONTAINER_heap_remove_node (lp->hn));
899 lp_trigger (lp,
900 h);
901 }
902 }
903 }
629 GNUNET_assert (0 == 904 GNUNET_assert (0 ==
630 pthread_mutex_unlock (&h->big_lock)); 905 pthread_mutex_unlock (&h->big_lock));
631 if ( (NULL != old) && 906 if ( (NULL != old) &&
@@ -884,6 +1159,7 @@ free_account (void *cls,
884{ 1159{
885 struct Account *account = val; 1160 struct Account *account = val;
886 1161
1162 GNUNET_assert (NULL == account->lp_head);
887 GNUNET_free (account->account_name); 1163 GNUNET_free (account->account_name);
888 GNUNET_free (account); 1164 GNUNET_free (account);
889 return GNUNET_OK; 1165 return GNUNET_OK;
@@ -898,6 +1174,11 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
898 GNUNET_SCHEDULER_cancel (h->mhd_task); 1174 GNUNET_SCHEDULER_cancel (h->mhd_task);
899 h->mhd_task = NULL; 1175 h->mhd_task = NULL;
900 } 1176 }
1177 if (NULL != h->lp_task)
1178 {
1179 GNUNET_SCHEDULER_cancel (h->lp_task);
1180 h->lp_task = NULL;
1181 }
901#if EPOLL_SUPPORT 1182#if EPOLL_SUPPORT
902 if (NULL != h->mhd_rfd) 1183 if (NULL != h->mhd_rfd)
903 { 1184 {
@@ -910,6 +1191,39 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
910 MHD_stop_daemon (h->mhd_bank); 1191 MHD_stop_daemon (h->mhd_bank);
911 h->mhd_bank = NULL; 1192 h->mhd_bank = NULL;
912 } 1193 }
1194 if (-1 != h->lp_event)
1195 {
1196 uint64_t val = 1;
1197 void *ret;
1198 struct LongPoller *lp;
1199
1200 GNUNET_assert (0 ==
1201 pthread_mutex_lock (&h->big_lock));
1202 h->in_shutdown = true;
1203 while (NULL != (lp = GNUNET_CONTAINER_heap_remove_root (h->lp_heap)))
1204 lp_trigger (lp,
1205 h);
1206 GNUNET_break (sizeof (val) ==
1207 write (h->lp_event,
1208 &val,
1209 sizeof (val)));
1210 GNUNET_assert (0 ==
1211 pthread_mutex_unlock (&h->big_lock));
1212 GNUNET_break (0 ==
1213 pthread_join (h->lp_thread,
1214 &ret));
1215 GNUNET_break (NULL == ret);
1216 GNUNET_break (0 == close (h->lp_event));
1217 h->lp_event = -1;
1218 }
1219 else
1220 {
1221 struct LongPoller *lp;
1222
1223 while (NULL != (lp = GNUNET_CONTAINER_heap_remove_root (h->lp_heap)))
1224 lp_trigger (lp,
1225 h);
1226 }
913 if (NULL != h->accounts) 1227 if (NULL != h->accounts)
914 { 1228 {
915 GNUNET_CONTAINER_multihashmap_iterate (h->accounts, 1229 GNUNET_CONTAINER_multihashmap_iterate (h->accounts,
@@ -919,6 +1233,7 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
919 } 1233 }
920 GNUNET_CONTAINER_multihashmap_destroy (h->uuid_map); 1234 GNUNET_CONTAINER_multihashmap_destroy (h->uuid_map);
921 GNUNET_CONTAINER_multipeermap_destroy (h->rpubs); 1235 GNUNET_CONTAINER_multipeermap_destroy (h->rpubs);
1236 GNUNET_CONTAINER_heap_destroy (h->lp_heap);
922 GNUNET_assert (0 == 1237 GNUNET_assert (0 ==
923 pthread_mutex_destroy (&h->big_lock)); 1238 pthread_mutex_destroy (&h->big_lock));
924 GNUNET_assert (0 == 1239 GNUNET_assert (0 ==
@@ -960,6 +1275,10 @@ handle_mhd_completion_callback (void *cls,
960 (void) cls; 1275 (void) cls;
961 (void) connection; 1276 (void) connection;
962 (void) toe; 1277 (void) toe;
1278 if (NULL == *con_cls)
1279 return;
1280 if (&special_ptr == *con_cls)
1281 return;
963 GNUNET_JSON_post_parser_cleanup (*con_cls); 1282 GNUNET_JSON_post_parser_cleanup (*con_cls);
964 *con_cls = NULL; 1283 *con_cls = NULL;
965} 1284}
@@ -988,7 +1307,6 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
988 json_t *json; 1307 json_t *json;
989 uint64_t row_id; 1308 uint64_t row_id;
990 struct GNUNET_TIME_Absolute timestamp; 1309 struct GNUNET_TIME_Absolute timestamp;
991 enum GNUNET_GenericReturnValue ret;
992 1310
993 pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX, 1311 pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
994 connection, 1312 connection,
@@ -1017,6 +1335,7 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
1017 struct TALER_Amount amount; 1335 struct TALER_Amount amount;
1018 struct TALER_ReservePublicKeyP reserve_pub; 1336 struct TALER_ReservePublicKeyP reserve_pub;
1019 char *debit; 1337 char *debit;
1338 enum GNUNET_GenericReturnValue ret;
1020 struct GNUNET_JSON_Specification spec[] = { 1339 struct GNUNET_JSON_Specification spec[] = {
1021 GNUNET_JSON_spec_fixed_auto ("reserve_pub", 1340 GNUNET_JSON_spec_fixed_auto ("reserve_pub",
1022 &reserve_pub), 1341 &reserve_pub),
@@ -1029,14 +1348,13 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
1029 }; 1348 };
1030 1349
1031 if (GNUNET_OK != 1350 if (GNUNET_OK !=
1032 GNUNET_JSON_parse (json, 1351 (ret = TALER_MHD_parse_json_data (connection,
1033 spec, 1352 json,
1034 NULL, NULL)) 1353 spec)))
1035 { 1354 {
1036 GNUNET_break (0); 1355 GNUNET_break_op (0);
1037 json_decref (json); 1356 json_decref (json);
1038 /* We're fakebank, no need for nice error handling */ 1357 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
1039 return MHD_NO;
1040 } 1358 }
1041 if (0 != strcasecmp (amount.currency, 1359 if (0 != strcasecmp (amount.currency,
1042 h->currency)) 1360 h->currency))
@@ -1141,6 +1459,7 @@ handle_transfer (struct TALER_FAKEBANK_Handle *h,
1141 char *credit; 1459 char *credit;
1142 const char *base_url; 1460 const char *base_url;
1143 struct TALER_Amount amount; 1461 struct TALER_Amount amount;
1462 enum GNUNET_GenericReturnValue ret;
1144 struct GNUNET_JSON_Specification spec[] = { 1463 struct GNUNET_JSON_Specification spec[] = {
1145 GNUNET_JSON_spec_fixed_auto ("request_uid", 1464 GNUNET_JSON_spec_fixed_auto ("request_uid",
1146 &uuid), 1465 &uuid),
@@ -1157,14 +1476,13 @@ handle_transfer (struct TALER_FAKEBANK_Handle *h,
1157 }; 1476 };
1158 1477
1159 if (GNUNET_OK != 1478 if (GNUNET_OK !=
1160 GNUNET_JSON_parse (json, 1479 (ret = TALER_MHD_parse_json_data (connection,
1161 spec, 1480 json,
1162 NULL, NULL)) 1481 spec)))
1163 { 1482 {
1164 GNUNET_break (0); 1483 GNUNET_break_op (0);
1165 json_decref (json); 1484 json_decref (json);
1166 /* We are fakebank, no need for nice error handling */ 1485 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
1167 return MHD_NO;
1168 } 1486 }
1169 { 1487 {
1170 int ret; 1488 int ret;
@@ -1223,20 +1541,17 @@ handle_transfer (struct TALER_FAKEBANK_Handle *h,
1223 * 1541 *
1224 * @param h the fakebank handle 1542 * @param h the fakebank handle
1225 * @param connection the connection 1543 * @param connection the connection
1226 * @param con_cls place to store state, not used
1227 * @return MHD result code 1544 * @return MHD result code
1228 */ 1545 */
1229static MHD_RESULT 1546static MHD_RESULT
1230handle_home_page (struct TALER_FAKEBANK_Handle *h, 1547handle_home_page (struct TALER_FAKEBANK_Handle *h,
1231 struct MHD_Connection *connection, 1548 struct MHD_Connection *connection)
1232 void **con_cls)
1233{ 1549{
1234 MHD_RESULT ret; 1550 MHD_RESULT ret;
1235 struct MHD_Response *resp; 1551 struct MHD_Response *resp;
1236#define HELLOMSG "Hello, Fakebank!" 1552#define HELLOMSG "Hello, Fakebank!"
1237 1553
1238 (void) h; 1554 (void) h;
1239 (void) con_cls;
1240 resp = MHD_create_response_from_buffer ( 1555 resp = MHD_create_response_from_buffer (
1241 strlen (HELLOMSG), 1556 strlen (HELLOMSG),
1242 HELLOMSG, 1557 HELLOMSG,
@@ -1292,9 +1607,11 @@ struct HistoryArgs
1292 * @param h bank handle to work on 1607 * @param h bank handle to work on
1293 * @param connection MHD connection. 1608 * @param connection MHD connection.
1294 * @param[out] ha will contain the parsed values. 1609 * @param[out] ha will contain the parsed values.
1295 * @return #GNUNET_OK only if the parsing succeeds. 1610 * @return #GNUNET_OK only if the parsing succeeds,
1611 * #GNUNET_SYSERR if it failed,
1612 * #GNUNET_NO if it failed and an error was returned
1296 */ 1613 */
1297static int 1614static enum GNUNET_GenericReturnValue
1298parse_history_common_args (const struct TALER_FAKEBANK_Handle *h, 1615parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
1299 struct MHD_Connection *connection, 1616 struct MHD_Connection *connection,
1300 struct HistoryArgs *ha) 1617 struct HistoryArgs *ha)
@@ -1305,6 +1622,7 @@ parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
1305 unsigned long long lp_timeout; 1622 unsigned long long lp_timeout;
1306 unsigned long long sval; 1623 unsigned long long sval;
1307 long long d; 1624 long long d;
1625 char dummy;
1308 1626
1309 start = MHD_lookup_connection_value (connection, 1627 start = MHD_lookup_connection_value (connection,
1310 MHD_GET_ARGUMENT_KIND, 1628 MHD_GET_ARGUMENT_KIND,
@@ -1319,23 +1637,60 @@ parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
1319 lp_timeout = 0; 1637 lp_timeout = 0;
1320 if ( (NULL == delta) || 1638 if ( (NULL == delta) ||
1321 (1 != sscanf (delta, 1639 (1 != sscanf (delta,
1322 "%lld", 1640 "%lld%c",
1323 &d)) || 1641 &d,
1324 ( (NULL != long_poll_ms) && 1642 &dummy)) )
1325 (1 != sscanf (long_poll_ms,
1326 "%llu",
1327 &lp_timeout)) ) ||
1328 ( (NULL != start) &&
1329 (1 != sscanf (start,
1330 "%llu",
1331 &sval)) ) )
1332 { 1643 {
1333 /* Fail if one of the above failed. */ 1644 /* Fail if one of the above failed. */
1334 /* Invalid request, given that this is fakebank we impolitely 1645 /* Invalid request, given that this is fakebank we impolitely
1335 * just kill the connection instead of returning a nice error. 1646 * just kill the connection instead of returning a nice error.
1336 */ 1647 */
1337 GNUNET_break (0); 1648 GNUNET_break_op (0);
1338 return GNUNET_NO; 1649 return (MHD_YES ==
1650 TALER_MHD_reply_with_error (connection,
1651 MHD_HTTP_BAD_REQUEST,
1652 TALER_EC_GENERIC_PARAMETER_MALFORMED,
1653 "delta"))
1654 ? GNUNET_NO
1655 : GNUNET_SYSERR;
1656 }
1657 if ( (NULL != long_poll_ms) &&
1658 (1 != sscanf (long_poll_ms,
1659 "%llu%c",
1660 &lp_timeout,
1661 &dummy)) )
1662 {
1663 /* Fail if one of the above failed. */
1664 /* Invalid request, given that this is fakebank we impolitely
1665 * just kill the connection instead of returning a nice error.
1666 */
1667 GNUNET_break_op (0);
1668 return (MHD_YES ==
1669 TALER_MHD_reply_with_error (connection,
1670 MHD_HTTP_BAD_REQUEST,
1671 TALER_EC_GENERIC_PARAMETER_MALFORMED,
1672 "long_poll_ms"))
1673 ? GNUNET_NO
1674 : GNUNET_SYSERR;
1675 }
1676 if ( (NULL != start) &&
1677 (1 != sscanf (start,
1678 "%llu%c",
1679 &sval,
1680 &dummy)) )
1681 {
1682 /* Fail if one of the above failed. */
1683 /* Invalid request, given that this is fakebank we impolitely
1684 * just kill the connection instead of returning a nice error.
1685 */
1686 GNUNET_break_op (0);
1687 return (MHD_YES ==
1688 TALER_MHD_reply_with_error (connection,
1689 MHD_HTTP_BAD_REQUEST,
1690 TALER_EC_GENERIC_PARAMETER_MALFORMED,
1691 "start"))
1692 ? GNUNET_NO
1693 : GNUNET_SYSERR;
1339 } 1694 }
1340 if (NULL == start) 1695 if (NULL == start)
1341 ha->start_idx = (d > 0) ? 0 : h->serial_counter; 1696 ha->start_idx = (d > 0) ? 0 : h->serial_counter;
@@ -1344,8 +1699,14 @@ parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
1344 ha->delta = (int64_t) d; 1699 ha->delta = (int64_t) d;
1345 if (0 == ha->delta) 1700 if (0 == ha->delta)
1346 { 1701 {
1347 GNUNET_break (0); 1702 GNUNET_break_op (0);
1348 return GNUNET_NO; 1703 return (MHD_YES ==
1704 TALER_MHD_reply_with_error (connection,
1705 MHD_HTTP_BAD_REQUEST,
1706 TALER_EC_GENERIC_PARAMETER_MALFORMED,
1707 "delta"))
1708 ? GNUNET_NO
1709 : GNUNET_SYSERR;
1349 } 1710 }
1350 ha->lp_timeout 1711 ha->lp_timeout
1351 = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 1712 = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
@@ -1359,33 +1720,146 @@ parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
1359 1720
1360 1721
1361/** 1722/**
1723 * Task run when a long poller is about to time out.
1724 * Only used in single-threaded mode.
1725 *
1726 * @param cls a `struct TALER_FAKEBANK_Handle *`
1727 */
1728static void
1729lp_timeout (void *cls)
1730{
1731 struct TALER_FAKEBANK_Handle *h = cls;
1732 struct LongPoller *lp;
1733
1734 h->lp_task = NULL;
1735 while (NULL != (lp = GNUNET_CONTAINER_heap_peek (h->lp_heap)))
1736 {
1737 if (GNUNET_TIME_absolute_is_future (lp->timeout))
1738 break;
1739 GNUNET_assert (lp ==
1740 GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
1741 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1742 "Timeout reached for long poller %p\n",
1743 lp->conn);
1744 lp_trigger (lp,
1745 h);
1746 }
1747 if (NULL == lp)
1748 return;
1749 h->lp_task = GNUNET_SCHEDULER_add_at (lp->timeout,
1750 &lp_timeout,
1751 h);
1752}
1753
1754
1755/**
1756 * Reschedule the timeout task of @a h for time @a t.
1757 *
1758 * @param h fakebank handle
1759 * @param t when will the next connection timeout expire
1760 */
1761static void
1762reschedule_lp_timeout (struct TALER_FAKEBANK_Handle *h,
1763 struct GNUNET_TIME_Absolute t)
1764{
1765 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1766 "Scheduling timeout task for %s\n",
1767 GNUNET_STRINGS_absolute_time_to_string (t));
1768 if (-1 != h->lp_event)
1769 {
1770 uint64_t num = 1;
1771
1772 GNUNET_break (sizeof (num) ==
1773 write (h->lp_event,
1774 &num,
1775 sizeof (num)));
1776 }
1777 else
1778 {
1779 if (NULL != h->lp_task)
1780 GNUNET_SCHEDULER_cancel (h->lp_task);
1781 h->lp_task = GNUNET_SCHEDULER_add_at (t,
1782 &lp_timeout,
1783 h);
1784 }
1785}
1786
1787
1788/**
1789 * Start long-polling for @a connection and @a acc
1790 * for transfers in @a dir. Must be called with the
1791 * "big lock" held.
1792 *
1793 * @param[in,out] h fakebank handle
1794 * @param[in,out] connection to suspend
1795 * @param[in,out] acc account affected
1796 * @param lp_timeout how long to suspend
1797 * @param dir direction of transfers to watch for
1798 */
1799static void
1800start_lp (struct TALER_FAKEBANK_Handle *h,
1801 struct MHD_Connection *connection,
1802 struct Account *acc,
1803 struct GNUNET_TIME_Relative lp_timeout,
1804 enum LongPollType dir)
1805{
1806 struct LongPoller *lp;
1807 bool toc;
1808
1809 lp = GNUNET_new (struct LongPoller);
1810 lp->account = acc;
1811 lp->conn = connection;
1812 lp->timeout = GNUNET_TIME_relative_to_absolute (lp_timeout);
1813 lp->type = dir;
1814 lp->hn = GNUNET_CONTAINER_heap_insert (h->lp_heap,
1815 lp,
1816 lp->timeout.abs_value_us);
1817 toc = (lp ==
1818 GNUNET_CONTAINER_heap_peek (h->lp_heap));
1819 GNUNET_CONTAINER_DLL_insert (acc->lp_head,
1820 acc->lp_tail,
1821 lp);
1822 MHD_suspend_connection (connection);
1823 if (toc)
1824 reschedule_lp_timeout (h,
1825 lp->timeout);
1826
1827}
1828
1829
1830/**
1362 * Handle incoming HTTP request for /history/outgoing 1831 * Handle incoming HTTP request for /history/outgoing
1363 * 1832 *
1364 * @param h the fakebank handle 1833 * @param h the fakebank handle
1365 * @param connection the connection 1834 * @param connection the connection
1366 * @param account which account the request is about 1835 * @param account which account the request is about
1367 * @return MHD result code 1836 * @param con_cls closure for request (NULL or &special_ptr)
1368 */ 1837 */
1369static MHD_RESULT 1838static MHD_RESULT
1370handle_debit_history (struct TALER_FAKEBANK_Handle *h, 1839handle_debit_history (struct TALER_FAKEBANK_Handle *h,
1371 struct MHD_Connection *connection, 1840 struct MHD_Connection *connection,
1372 const char *account) 1841 const char *account,
1842 void **con_cls)
1373{ 1843{
1374 struct HistoryArgs ha; 1844 struct HistoryArgs ha;
1375 struct Account *acc; 1845 struct Account *acc;
1376 struct Transaction *pos; 1846 struct Transaction *pos;
1377 json_t *history; 1847 json_t *history;
1378 char *debit_payto; 1848 char *debit_payto;
1849 enum GNUNET_GenericReturnValue ret;
1379 1850
1851 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1852 "Handling /history/outgoing connection %p\n",
1853 connection);
1380 if (GNUNET_OK != 1854 if (GNUNET_OK !=
1381 parse_history_common_args (h, 1855 (ret = parse_history_common_args (h,
1382 connection, 1856 connection,
1383 &ha)) 1857 &ha)))
1384 { 1858 {
1385 GNUNET_break (0); 1859 return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
1386 return MHD_NO;
1387 } 1860 }
1388 1861 if (&special_ptr == *con_cls)
1862 ha.lp_timeout = GNUNET_TIME_UNIT_ZERO;
1389 acc = lookup_account (h, 1863 acc = lookup_account (h,
1390 account); 1864 account);
1391 GNUNET_asprintf (&debit_payto, 1865 GNUNET_asprintf (&debit_payto,
@@ -1430,16 +1904,29 @@ handle_debit_history (struct TALER_FAKEBANK_Handle *h,
1430 if ( (NULL == t) || 1904 if ( (NULL == t) ||
1431 overflow) 1905 overflow)
1432 { 1906 {
1907 GNUNET_free (debit_payto);
1908 if (GNUNET_TIME_relative_is_zero (ha.lp_timeout) &&
1909 (0 < ha.delta))
1910 {
1911 GNUNET_assert (0 ==
1912 pthread_mutex_unlock (&h->big_lock));
1913 return TALER_MHD_REPLY_JSON_PACK (
1914 connection,
1915 MHD_HTTP_OK,
1916 GNUNET_JSON_pack_array_steal (
1917 "outgoing_transactions",
1918 history));
1919 }
1920 *con_cls = &special_ptr;
1921 start_lp (h,
1922 connection,
1923 acc,
1924 ha.lp_timeout,
1925 LP_DEBIT);
1433 GNUNET_assert (0 == 1926 GNUNET_assert (0 ==
1434 pthread_mutex_unlock (&h->big_lock)); 1927 pthread_mutex_unlock (&h->big_lock));
1435 GNUNET_free (debit_payto); 1928 json_decref (history);
1436 /* FIXME: suspend for long-polling instead */ 1929 return MHD_YES;
1437 return TALER_MHD_REPLY_JSON_PACK (
1438 connection,
1439 MHD_HTTP_OK,
1440 GNUNET_JSON_pack_array_steal (
1441 "outgoing_transactions",
1442 history));
1443 } 1930 }
1444 if (t->debit_account != acc) 1931 if (t->debit_account != acc)
1445 { 1932 {
@@ -1524,6 +2011,21 @@ handle_debit_history (struct TALER_FAKEBANK_Handle *h,
1524 if (0 < ha.delta) 2011 if (0 < ha.delta)
1525 pos = pos->next_out; 2012 pos = pos->next_out;
1526 } 2013 }
2014 if ( (0 == json_array_size (history)) &&
2015 (! GNUNET_TIME_relative_is_zero (ha.lp_timeout)) &&
2016 (0 < ha.delta))
2017 {
2018 *con_cls = &special_ptr;
2019 start_lp (h,
2020 connection,
2021 acc,
2022 ha.lp_timeout,
2023 LP_DEBIT);
2024 GNUNET_assert (0 ==
2025 pthread_mutex_unlock (&h->big_lock));
2026 json_decref (history);
2027 return MHD_YES;
2028 }
1527 GNUNET_assert (0 == 2029 GNUNET_assert (0 ==
1528 pthread_mutex_unlock (&h->big_lock)); 2030 pthread_mutex_unlock (&h->big_lock));
1529 GNUNET_free (debit_payto); 2031 GNUNET_free (debit_payto);
@@ -1546,22 +2048,29 @@ handle_debit_history (struct TALER_FAKEBANK_Handle *h,
1546static MHD_RESULT 2048static MHD_RESULT
1547handle_credit_history (struct TALER_FAKEBANK_Handle *h, 2049handle_credit_history (struct TALER_FAKEBANK_Handle *h,
1548 struct MHD_Connection *connection, 2050 struct MHD_Connection *connection,
1549 const char *account) 2051 const char *account,
2052 void **con_cls)
1550{ 2053{
1551 struct HistoryArgs ha; 2054 struct HistoryArgs ha;
1552 struct Account *acc; 2055 struct Account *acc;
1553 const struct Transaction *pos; 2056 const struct Transaction *pos;
1554 json_t *history; 2057 json_t *history;
1555 char *credit_payto; 2058 char *credit_payto;
2059 enum GNUNET_GenericReturnValue ret;
1556 2060
2061 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2062 "Handling /history/incoming connection %p\n",
2063 connection);
1557 if (GNUNET_OK != 2064 if (GNUNET_OK !=
1558 parse_history_common_args (h, 2065 (ret = parse_history_common_args (h,
1559 connection, 2066 connection,
1560 &ha)) 2067 &ha)))
1561 { 2068 {
1562 GNUNET_break (0); 2069 return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
1563 return MHD_NO;
1564 } 2070 }
2071 if (&special_ptr == *con_cls)
2072 ha.lp_timeout = GNUNET_TIME_UNIT_ZERO;
2073 *con_cls = &special_ptr;
1565 acc = lookup_account (h, 2074 acc = lookup_account (h,
1566 account); 2075 account);
1567 history = json_array (); 2076 history = json_array ();
@@ -1601,15 +2110,28 @@ handle_credit_history (struct TALER_FAKEBANK_Handle *h,
1601 if ( (NULL == t) || 2110 if ( (NULL == t) ||
1602 overflow) 2111 overflow)
1603 { 2112 {
2113 GNUNET_free (credit_payto);
2114 if (GNUNET_TIME_relative_is_zero (ha.lp_timeout) &&
2115 (0 < ha.delta))
2116 {
2117 GNUNET_assert (0 ==
2118 pthread_mutex_unlock (&h->big_lock));
2119 return TALER_MHD_REPLY_JSON_PACK (connection,
2120 MHD_HTTP_OK,
2121 GNUNET_JSON_pack_array_steal (
2122 "incoming_transactions",
2123 history));
2124 }
2125 *con_cls = &special_ptr;
2126 start_lp (h,
2127 connection,
2128 acc,
2129 ha.lp_timeout,
2130 LP_CREDIT);
1604 GNUNET_assert (0 == 2131 GNUNET_assert (0 ==
1605 pthread_mutex_unlock (&h->big_lock)); 2132 pthread_mutex_unlock (&h->big_lock));
1606 GNUNET_free (credit_payto); 2133 json_decref (history);
1607 /* FIXME: suspend for long-polling instead */ 2134 return MHD_YES;
1608 return TALER_MHD_REPLY_JSON_PACK (connection,
1609 MHD_HTTP_OK,
1610 GNUNET_JSON_pack_array_steal (
1611 "incoming_transactions",
1612 history));
1613 } 2135 }
1614 if (skip) 2136 if (skip)
1615 { 2137 {
@@ -1681,6 +2203,21 @@ handle_credit_history (struct TALER_FAKEBANK_Handle *h,
1681 if (0 < ha.delta) 2203 if (0 < ha.delta)
1682 pos = pos->next_in; 2204 pos = pos->next_in;
1683 } 2205 }
2206 if ( (0 == json_array_size (history)) &&
2207 (! GNUNET_TIME_relative_is_zero (ha.lp_timeout)) &&
2208 (0 < ha.delta))
2209 {
2210 *con_cls = &special_ptr;
2211 start_lp (h,
2212 connection,
2213 acc,
2214 ha.lp_timeout,
2215 LP_CREDIT);
2216 GNUNET_assert (0 ==
2217 pthread_mutex_unlock (&h->big_lock));
2218 json_decref (history);
2219 return MHD_YES;
2220 }
1684 GNUNET_assert (0 == 2221 GNUNET_assert (0 ==
1685 pthread_mutex_unlock (&h->big_lock)); 2222 pthread_mutex_unlock (&h->big_lock));
1686 GNUNET_free (credit_payto); 2223 GNUNET_free (credit_payto);
@@ -1702,7 +2239,7 @@ handle_credit_history (struct TALER_FAKEBANK_Handle *h,
1702 * @param account which account should process the request 2239 * @param account which account should process the request
1703 * @param upload_data request data 2240 * @param upload_data request data
1704 * @param upload_data_size size of @a upload_data in bytes 2241 * @param upload_data_size size of @a upload_data in bytes
1705 * @param con_cls closure for request (a `struct Buffer *`) 2242 * @param con_cls closure
1706 * @return MHD result code 2243 * @return MHD result code
1707 */ 2244 */
1708static MHD_RESULT 2245static MHD_RESULT
@@ -1727,18 +2264,19 @@ serve (struct TALER_FAKEBANK_Handle *h,
1727 (NULL != account) ) 2264 (NULL != account) )
1728 return handle_credit_history (h, 2265 return handle_credit_history (h,
1729 connection, 2266 connection,
1730 account); 2267 account,
2268 con_cls);
1731 if ( (0 == strcmp (url, 2269 if ( (0 == strcmp (url,
1732 "/history/outgoing")) && 2270 "/history/outgoing")) &&
1733 (NULL != account) ) 2271 (NULL != account) )
1734 return handle_debit_history (h, 2272 return handle_debit_history (h,
1735 connection, 2273 connection,
1736 account); 2274 account,
2275 con_cls);
1737 if (0 == strcmp (url, 2276 if (0 == strcmp (url,
1738 "/")) 2277 "/"))
1739 return handle_home_page (h, 2278 return handle_home_page (h,
1740 connection, 2279 connection);
1741 con_cls);
1742 } 2280 }
1743 else if (0 == strcasecmp (method, 2281 else if (0 == strcasecmp (method,
1744 MHD_HTTP_METHOD_POST)) 2282 MHD_HTTP_METHOD_POST))
@@ -1762,12 +2300,15 @@ serve (struct TALER_FAKEBANK_Handle *h,
1762 con_cls); 2300 con_cls);
1763 } 2301 }
1764 /* Unexpected URL path, just close the connection. */ 2302 /* Unexpected URL path, just close the connection. */
1765 /* We're rather impolite here, but it's a testcase. */
1766 TALER_LOG_ERROR ("Breaking URL: %s %s\n", 2303 TALER_LOG_ERROR ("Breaking URL: %s %s\n",
1767 method, 2304 method,
1768 url); 2305 url);
1769 GNUNET_break_op (0); 2306 GNUNET_break_op (0);
1770 return MHD_NO; 2307 return TALER_MHD_reply_with_error (
2308 connection,
2309 MHD_HTTP_NOT_FOUND,
2310 TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
2311 url);
1771} 2312}
1772 2313
1773 2314
@@ -1781,7 +2322,7 @@ serve (struct TALER_FAKEBANK_Handle *h,
1781 * @param version HTTP version (ignored) 2322 * @param version HTTP version (ignored)
1782 * @param upload_data request data 2323 * @param upload_data request data
1783 * @param upload_data_size size of @a upload_data in bytes 2324 * @param upload_data_size size of @a upload_data in bytes
1784 * @param con_cls closure for request (a `struct Buffer *`) 2325 * @param con_cls closure for request
1785 * @return MHD result code 2326 * @return MHD result code
1786 */ 2327 */
1787static MHD_RESULT 2328static MHD_RESULT
@@ -1823,15 +2364,6 @@ handle_mhd_request (void *cls,
1823} 2364}
1824 2365
1825 2366
1826/**
1827 * Task run whenever HTTP server operations are pending.
1828 *
1829 * @param cls the `struct TALER_FAKEBANK_Handle`
1830 */
1831static void
1832run_mhd (void *cls);
1833
1834
1835#if EPOLL_SUPPORT 2367#if EPOLL_SUPPORT
1836/** 2368/**
1837 * Schedule MHD. This function should be called initially when an 2369 * Schedule MHD. This function should be called initially when an
@@ -1982,6 +2514,7 @@ TALER_FAKEBANK_start2 (uint16_t port,
1982 } 2514 }
1983 GNUNET_assert (strlen (currency) < TALER_CURRENCY_LEN); 2515 GNUNET_assert (strlen (currency) < TALER_CURRENCY_LEN);
1984 h = GNUNET_new (struct TALER_FAKEBANK_Handle); 2516 h = GNUNET_new (struct TALER_FAKEBANK_Handle);
2517 h->lp_event = -1;
1985 h->port = port; 2518 h->port = port;
1986 h->ram_limit = ram_limit; 2519 h->ram_limit = ram_limit;
1987 h->serial_counter = 0; 2520 h->serial_counter = 0;
@@ -2027,6 +2560,7 @@ TALER_FAKEBANK_start2 (uint16_t port,
2027 TALER_FAKEBANK_stop (h); 2560 TALER_FAKEBANK_stop (h);
2028 return NULL; 2561 return NULL;
2029 } 2562 }
2563 h->lp_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
2030 h->currency = GNUNET_strdup (currency); 2564 h->currency = GNUNET_strdup (currency);
2031 GNUNET_asprintf (&h->my_baseurl, 2565 GNUNET_asprintf (&h->my_baseurl,
2032 "http://localhost:%u/", 2566 "http://localhost:%u/",
@@ -2061,6 +2595,28 @@ TALER_FAKEBANK_start2 (uint16_t port,
2061 } 2595 }
2062 else 2596 else
2063 { 2597 {
2598 h->lp_event = eventfd (0,
2599 EFD_CLOEXEC);
2600 if (-1 == h->lp_event)
2601 {
2602 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
2603 "eventfd");
2604 TALER_FAKEBANK_stop (h);
2605 return NULL;
2606 }
2607 if (0 !=
2608 pthread_create (&h->lp_thread,
2609 NULL,
2610 &lp_expiration_thread,
2611 h))
2612 {
2613 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
2614 "pthread_create");
2615 GNUNET_break (0 == close (h->lp_event));
2616 h->lp_event = -1;
2617 TALER_FAKEBANK_stop (h);
2618 return NULL;
2619 }
2064 h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG 2620 h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG
2065 | MHD_USE_AUTO_INTERNAL_THREAD 2621 | MHD_USE_AUTO_INTERNAL_THREAD
2066 | MHD_ALLOW_SUSPEND_RESUME 2622 | MHD_ALLOW_SUSPEND_RESUME