dynamic curl timeouts for /keys and /wire requests

This commit is contained in:
Florian Dold 2021-01-12 15:08:15 +01:00
parent 2518da8f45
commit 8f887a215e
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
3 changed files with 166 additions and 99 deletions

View File

@ -68,27 +68,6 @@
GNUNET_log (type, "Curl function `%s' has failed at `%s:%d' with error: %s", \ GNUNET_log (type, "Curl function `%s' has failed at `%s:%d' with error: %s", \
function, __FILE__, __LINE__, curl_easy_strerror (code)); function, __FILE__, __LINE__, curl_easy_strerror (code));
/**
* Stages of initialization for the `struct TALER_EXCHANGE_Handle`
*/
enum ExchangeHandleState
{
/**
* Just allocated.
*/
MHS_INIT = 0,
/**
* Obtained the exchange's certification data and keys.
*/
MHS_CERT = 1,
/**
* Failed to initialize (fatal).
*/
MHS_FAILED = 2
};
/** /**
* Data for the request to get the /keys of a exchange. * Data for the request to get the /keys of a exchange.
@ -145,83 +124,6 @@ struct TEAH_AuditorListEntry
}; };
/**
* Handle to the exchange
*/
struct TALER_EXCHANGE_Handle
{
/**
* The context of this handle
*/
struct GNUNET_CURL_Context *ctx;
/**
* The URL of the exchange (i.e. "http://exchange.taler.net/")
*/
char *url;
/**
* Function to call with the exchange's certification data,
* NULL if this has already been done.
*/
TALER_EXCHANGE_CertificationCallback cert_cb;
/**
* Closure to pass to @e cert_cb.
*/
void *cert_cb_cls;
/**
* Data for the request to get the /keys of a exchange,
* NULL once we are past stage #MHS_INIT.
*/
struct KeysRequest *kr;
/**
* Task for retrying /keys request.
*/
struct GNUNET_SCHEDULER_Task *retry_task;
/**
* Raw key data of the exchange, only valid if
* @e handshake_complete is past stage #MHS_CERT.
*/
json_t *key_data_raw;
/**
* Head of DLL of auditors of this exchange.
*/
struct TEAH_AuditorListEntry *auditors_head;
/**
* Tail of DLL of auditors of this exchange.
*/
struct TEAH_AuditorListEntry *auditors_tail;
/**
* Key data of the exchange, only valid if
* @e handshake_complete is past stage #MHS_CERT.
*/
struct TALER_EXCHANGE_Keys key_data;
/**
* Retry /keys frequency.
*/
struct GNUNET_TIME_Relative retry_delay;
/**
* When does @e key_data expire?
*/
struct GNUNET_TIME_Absolute key_data_expiration;
/**
* Stage of the exchange's initialization routines.
*/
enum ExchangeHandleState state;
};
/* ***************** Internal /keys fetching ************* */ /* ***************** Internal /keys fetching ************* */
/** /**
@ -250,6 +152,7 @@ struct KeysRequest
*/ */
struct GNUNET_TIME_Absolute expire; struct GNUNET_TIME_Absolute expire;
struct GNUNET_SCHEDULER_Task *timeout_task;
}; };
@ -1201,6 +1104,8 @@ keys_completed_cb (void *cls,
{ {
case 0: case 0:
free_keys_request (kr); free_keys_request (kr);
/* FIXME: Maybe we should only increment when we know it's a timeout? */
exchange->keys_error_count++;
exchange->kr = NULL; exchange->kr = NULL;
GNUNET_assert (NULL == exchange->retry_task); GNUNET_assert (NULL == exchange->retry_task);
exchange->retry_delay = EXCHANGE_LIB_BACKOFF (exchange->retry_delay); exchange->retry_delay = EXCHANGE_LIB_BACKOFF (exchange->retry_delay);
@ -1209,6 +1114,7 @@ keys_completed_cb (void *cls,
exchange); exchange);
return; return;
case MHD_HTTP_OK: case MHD_HTTP_OK:
exchange->keys_error_count = 0;
if (NULL == j) if (NULL == j)
{ {
response_code = 0; response_code = 0;
@ -1290,6 +1196,8 @@ keys_completed_cb (void *cls,
exchange->retry_delay = GNUNET_TIME_UNIT_ZERO; exchange->retry_delay = GNUNET_TIME_UNIT_ZERO;
break; break;
default: default:
if (MHD_HTTP_GATEWAY_TIMEOUT == response_code)
exchange->keys_error_count++;
hr.ec = TALER_JSON_get_error_code (j); hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@ -1882,6 +1790,20 @@ TALER_EXCHANGE_connect (
} }
/**
* Compute the network timeout for the next request to /keys.
*
* @param exchange the exchange handle
* @returns the timeout in seconds (for use by CURL)
*/
static long
get_keys_timeout_seconds (struct TALER_EXCHANGE_Handle *exchange)
{
return GNUNET_MIN (60,
5 + (1L << exchange->keys_error_count));
}
/** /**
* Initiate download of /keys from the exchange. * Initiate download of /keys from the exchange.
* *
@ -1936,7 +1858,7 @@ request_keys (void *cls)
GNUNET_break (CURLE_OK == GNUNET_break (CURLE_OK ==
curl_easy_setopt (eh, curl_easy_setopt (eh,
CURLOPT_TIMEOUT, CURLOPT_TIMEOUT,
(long) 300)); get_keys_timeout_seconds (exchange)));
GNUNET_assert (CURLE_OK == GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh, curl_easy_setopt (eh,
CURLOPT_HEADERFUNCTION, CURLOPT_HEADERFUNCTION,

View File

@ -58,6 +58,118 @@ struct TEAH_AuditorInteractionEntry
struct TALER_AUDITOR_DepositConfirmationHandle *dch; struct TALER_AUDITOR_DepositConfirmationHandle *dch;
}; };
/**
* Stages of initialization for the `struct TALER_EXCHANGE_Handle`
*/
enum ExchangeHandleState
{
/**
* Just allocated.
*/
MHS_INIT = 0,
/**
* Obtained the exchange's certification data and keys.
*/
MHS_CERT = 1,
/**
* Failed to initialize (fatal).
*/
MHS_FAILED = 2
};
/**
* Handle to the exchange
*/
struct TALER_EXCHANGE_Handle
{
/**
* The context of this handle
*/
struct GNUNET_CURL_Context *ctx;
/**
* The URL of the exchange (i.e. "http://exchange.taler.net/")
*/
char *url;
/**
* Function to call with the exchange's certification data,
* NULL if this has already been done.
*/
TALER_EXCHANGE_CertificationCallback cert_cb;
/**
* Closure to pass to @e cert_cb.
*/
void *cert_cb_cls;
/**
* Data for the request to get the /keys of a exchange,
* NULL once we are past stage #MHS_INIT.
*/
struct KeysRequest *kr;
/**
* Task for retrying /keys request.
*/
struct GNUNET_SCHEDULER_Task *retry_task;
/**
* Raw key data of the exchange, only valid if
* @e handshake_complete is past stage #MHS_CERT.
*/
json_t *key_data_raw;
/**
* Head of DLL of auditors of this exchange.
*/
struct TEAH_AuditorListEntry *auditors_head;
/**
* Tail of DLL of auditors of this exchange.
*/
struct TEAH_AuditorListEntry *auditors_tail;
/**
* Key data of the exchange, only valid if
* @e handshake_complete is past stage #MHS_CERT.
*/
struct TALER_EXCHANGE_Keys key_data;
/**
* Retry /keys frequency.
*/
struct GNUNET_TIME_Relative retry_delay;
/**
* When does @e key_data expire?
*/
struct GNUNET_TIME_Absolute key_data_expiration;
/**
* Number of subsequent failed requests to /keys.
*
* Used to compute the CURL timeout for the request.
*/
unsigned int keys_error_count;
/**
* Number of subsequent failed requests to /wire.
*
* Used to compute the CURL timeout for the request.
*/
unsigned int wire_error_count;
/**
* Stage of the exchange's initialization routines.
*/
enum ExchangeHandleState state;
};
/** /**
* Function called for each auditor to give us a chance to possibly * Function called for each auditor to give us a chance to possibly
@ -111,6 +223,15 @@ struct GNUNET_CURL_Context *
TEAH_handle_to_context (struct TALER_EXCHANGE_Handle *h); TEAH_handle_to_context (struct TALER_EXCHANGE_Handle *h);
/**
* Check if the handle is ready to process requests.
*
* @param h the exchange handle to query
* @return #GNUNET_YES if we are ready, #GNUNET_NO if not
*/
int
TEAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h);
/** /**
* Check if the handle is ready to process requests. * Check if the handle is ready to process requests.
* *

View File

@ -219,6 +219,8 @@ handle_wire_finished (void *cls,
{ {
case 0: case 0:
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
/* FIXME: Maybe we should only increment when we know it's a timeout? */
wh->exchange->wire_error_count++;
break; break;
case MHD_HTTP_OK: case MHD_HTTP_OK:
{ {
@ -233,6 +235,8 @@ handle_wire_finished (void *cls,
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
wh->exchange->wire_error_count = 0;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_JSON_parse (j, GNUNET_JSON_parse (j,
spec, spec,
@ -356,6 +360,8 @@ handle_wire_finished (void *cls,
break; break;
default: default:
/* unexpected response code */ /* unexpected response code */
if (MHD_HTTP_GATEWAY_TIMEOUT == response_code)
wh->exchange->wire_error_count++;
GNUNET_break_op (0); GNUNET_break_op (0);
hr.ec = TALER_JSON_get_error_code (j); hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); hr.hint = TALER_JSON_get_error_hint (j);
@ -374,6 +380,20 @@ handle_wire_finished (void *cls,
} }
/**
* Compute the network timeout for the next request to /wire.
*
* @param exchange the exchange handle
* @returns the timeout in seconds (for use by CURL)
*/
static long
get_wire_timeout_seconds (struct TALER_EXCHANGE_Handle *exchange)
{
return GNUNET_MIN (60,
5 + (1L << exchange->wire_error_count));
}
/** /**
* Obtain information about a exchange's wire instructions. * Obtain information about a exchange's wire instructions.
* A exchange may provide wire instructions for creating * A exchange may provide wire instructions for creating
@ -416,6 +436,10 @@ TALER_EXCHANGE_wire (struct TALER_EXCHANGE_Handle *exchange,
wh->url = TEAH_path_to_url (exchange, wh->url = TEAH_path_to_url (exchange,
"/wire"); "/wire");
eh = TALER_EXCHANGE_curl_easy_get_ (wh->url); eh = TALER_EXCHANGE_curl_easy_get_ (wh->url);
GNUNET_break (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_TIMEOUT,
get_wire_timeout_seconds (wh->exchange)));
if (NULL == eh) if (NULL == eh)
{ {
GNUNET_break (0); GNUNET_break (0);