add Etag and 'expires' to /wire

This commit is contained in:
Christian Grothoff 2022-05-12 14:15:02 +02:00
parent 6cf4a068ad
commit 75d9584e28
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
5 changed files with 145 additions and 99 deletions

View File

@ -1656,58 +1656,6 @@ add_denom_key_cb (void *cls,
} }
/**
* Produce HTTP "Date:" header.
*
* @param at time to write to @a date
* @param[out] date where to write the header, with
* at least 128 bytes available space.
*/
static void
get_date_string (struct GNUNET_TIME_Absolute at,
char date[128])
{
static const char *const days[] =
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char *const mons[] =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
"Nov", "Dec"};
struct tm now;
time_t t;
#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
! defined(HAVE_GMTIME_R)
struct tm*pNow;
#endif
date[0] = 0;
t = (time_t) (at.abs_value_us / 1000LL / 1000LL);
#if defined(HAVE_C11_GMTIME_S)
if (NULL == gmtime_s (&t, &now))
return;
#elif defined(HAVE_W32_GMTIME_S)
if (0 != gmtime_s (&now, &t))
return;
#elif defined(HAVE_GMTIME_R)
if (NULL == gmtime_r (&t, &now))
return;
#else
pNow = gmtime (&t);
if (NULL == pNow)
return;
now = *pNow;
#endif
sprintf (date,
"%3s, %02u %3s %04u %02u:%02u:%02u GMT",
days[now.tm_wday % 7],
(unsigned int) now.tm_mday,
mons[now.tm_mon % 12],
(unsigned int) (1900 + now.tm_year),
(unsigned int) now.tm_hour,
(unsigned int) now.tm_min,
(unsigned int) now.tm_sec);
}
/** /**
* Add the headers we want to set for every /keys response. * Add the headers we want to set for every /keys response.
* *
@ -1726,8 +1674,8 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh,
MHD_add_response_header (response, MHD_add_response_header (response,
MHD_HTTP_HEADER_CONTENT_TYPE, MHD_HTTP_HEADER_CONTENT_TYPE,
"application/json")); "application/json"));
get_date_string (ksh->reload_time.abs_time, TALER_MHD_get_date_string (ksh->reload_time.abs_time,
dat); dat);
GNUNET_break (MHD_YES == GNUNET_break (MHD_YES ==
MHD_add_response_header (response, MHD_add_response_header (response,
MHD_HTTP_HEADER_LAST_MODIFIED, MHD_HTTP_HEADER_LAST_MODIFIED,
@ -1742,8 +1690,8 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh,
ksh->rekey_frequency); ksh->rekey_frequency);
a = GNUNET_TIME_relative_to_absolute (r); a = GNUNET_TIME_relative_to_absolute (r);
m = GNUNET_TIME_absolute_to_timestamp (a); m = GNUNET_TIME_absolute_to_timestamp (a);
get_date_string (m.abs_time, TALER_MHD_get_date_string (m.abs_time,
dat); dat);
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Setting /keys 'Expires' header to '%s'\n", "Setting /keys 'Expires' header to '%s'\n",
dat); dat);

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2015-2021 Taler Systems SA Copyright (C) 2015-2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software terms of the GNU Affero General Public License as published by the Free Software
@ -72,9 +72,9 @@ struct WireFeeSet
struct WireStateHandle struct WireStateHandle
{ {
/** /**
* Cached JSON for /wire response. * Cached reply for /wire response.
*/ */
json_t *wire_reply; struct MHD_Response *wire_reply;
/** /**
* head of DLL of wire fees. * head of DLL of wire fees.
@ -136,7 +136,7 @@ destroy_wire_state (struct WireStateHandle *wsh)
GNUNET_free (wfs->method); GNUNET_free (wfs->method);
GNUNET_free (wfs); GNUNET_free (wfs);
} }
json_decref (wsh->wire_reply); MHD_destroy_response (wsh->wire_reply);
GNUNET_free (wsh); GNUNET_free (wsh);
} }
@ -203,28 +203,6 @@ TEH_wire_done ()
} }
/**
* Create standard JSON response format using @a ec and @a detail.
*
* @param ec error code to return
* @param detail optional detail text to return, can be NULL
* @return JSON response
*/
static json_t *
make_ec_reply (enum TALER_ErrorCode ec,
const char *detail)
{
return GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("code",
ec),
GNUNET_JSON_pack_string ("hint",
TALER_ErrorCode_get_hint (ec)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("detail",
detail)));
}
/** /**
* Add information about a wire account to @a cls. * Add information about a wire account to @a cls.
* *
@ -274,6 +252,18 @@ struct AddContext
* Array to append the fee to. * Array to append the fee to.
*/ */
json_t *a; json_t *a;
/**
* Context we hash "everything" we add into. This is used
* to compute the etag. Technically, we only hash the
* master_sigs, as they imply the rest.
*/
struct GNUNET_HashContext *hc;
/**
* Set to the maximum end-date seen.
*/
struct GNUNET_TIME_Absolute max_seen;
}; };
@ -297,6 +287,11 @@ add_wire_fee (void *cls,
struct AddContext *ac = cls; struct AddContext *ac = cls;
struct WireFeeSet *wfs; struct WireFeeSet *wfs;
GNUNET_CRYPTO_hash_context_read (ac->hc,
master_sig,
sizeof (*master_sig));
ac->max_seen = GNUNET_TIME_absolute_max (ac->max_seen,
end_date.abs_time);
wfs = GNUNET_new (struct WireFeeSet); wfs = GNUNET_new (struct WireFeeSet);
wfs->start_date = start_date; wfs->start_date = start_date;
wfs->end_date = end_date; wfs->end_date = end_date;
@ -341,6 +336,8 @@ build_wire_state (void)
uint64_t wg = wire_generation; /* must be obtained FIRST */ uint64_t wg = wire_generation; /* must be obtained FIRST */
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
struct WireStateHandle *wsh; struct WireStateHandle *wsh;
struct GNUNET_TIME_Absolute cache_expiration;
struct GNUNET_HashContext *hc;
wsh = GNUNET_new (struct WireStateHandle); wsh = GNUNET_new (struct WireStateHandle);
wsh->wire_generation = wg; wsh->wire_generation = wg;
@ -355,8 +352,8 @@ build_wire_state (void)
json_decref (wire_accounts_array); json_decref (wire_accounts_array);
wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
wsh->wire_reply wsh->wire_reply
= make_ec_reply (TALER_EC_GENERIC_DB_FETCH_FAILED, = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
"get_wire_accounts"); "get_wire_accounts");
return wsh; return wsh;
} }
if (0 == json_array_size (wire_accounts_array)) if (0 == json_array_size (wire_accounts_array))
@ -364,12 +361,14 @@ build_wire_state (void)
json_decref (wire_accounts_array); json_decref (wire_accounts_array);
wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
wsh->wire_reply wsh->wire_reply
= make_ec_reply (TALER_EC_EXCHANGE_WIRE_NO_ACCOUNTS_CONFIGURED, = TALER_MHD_make_error (TALER_EC_EXCHANGE_WIRE_NO_ACCOUNTS_CONFIGURED,
NULL); NULL);
return wsh; return wsh;
} }
wire_fee_object = json_object (); wire_fee_object = json_object ();
GNUNET_assert (NULL != wire_fee_object); GNUNET_assert (NULL != wire_fee_object);
cache_expiration = GNUNET_TIME_UNIT_ZERO_ABS;
hc = GNUNET_CRYPTO_hash_context_start ();
{ {
json_t *account; json_t *account;
size_t index; size_t index;
@ -385,10 +384,12 @@ build_wire_state (void)
{ {
wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
wsh->wire_reply wsh->wire_reply
= make_ec_reply (TALER_EC_EXCHANGE_WIRE_INVALID_PAYTO_CONFIGURED, = TALER_MHD_make_error (
payto_uri); TALER_EC_EXCHANGE_WIRE_INVALID_PAYTO_CONFIGURED,
payto_uri);
json_decref (wire_accounts_array); json_decref (wire_accounts_array);
json_decref (wire_fee_object); json_decref (wire_fee_object);
GNUNET_CRYPTO_hash_context_abort (hc);
return wsh; return wsh;
} }
if (NULL == json_object_get (wire_fee_object, if (NULL == json_object_get (wire_fee_object,
@ -397,7 +398,8 @@ build_wire_state (void)
struct AddContext ac = { struct AddContext ac = {
.wire_method = wire_method, .wire_method = wire_method,
.wsh = wsh, .wsh = wsh,
.a = json_array () .a = json_array (),
.hc = hc
}; };
GNUNET_assert (NULL != ac.a); GNUNET_assert (NULL != ac.a);
@ -414,8 +416,9 @@ build_wire_state (void)
GNUNET_free (wire_method); GNUNET_free (wire_method);
wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
wsh->wire_reply wsh->wire_reply
= make_ec_reply (TALER_EC_GENERIC_DB_FETCH_FAILED, = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
"get_wire_fees"); "get_wire_fees");
GNUNET_CRYPTO_hash_context_abort (hc);
return wsh; return wsh;
} }
if (0 == json_array_size (ac.a)) if (0 == json_array_size (ac.a))
@ -425,11 +428,14 @@ build_wire_state (void)
json_decref (wire_fee_object); json_decref (wire_fee_object);
wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
wsh->wire_reply wsh->wire_reply
= make_ec_reply (TALER_EC_EXCHANGE_WIRE_FEES_NOT_CONFIGURED, = TALER_MHD_make_error (TALER_EC_EXCHANGE_WIRE_FEES_NOT_CONFIGURED,
wire_method); wire_method);
GNUNET_free (wire_method); GNUNET_free (wire_method);
GNUNET_CRYPTO_hash_context_abort (hc);
return wsh; return wsh;
} }
cache_expiration = GNUNET_TIME_absolute_min (ac.max_seen,
cache_expiration);
GNUNET_assert (0 == GNUNET_assert (0 ==
json_object_set_new (wire_fee_object, json_object_set_new (wire_fee_object,
wire_method, wire_method,
@ -438,13 +444,48 @@ build_wire_state (void)
GNUNET_free (wire_method); GNUNET_free (wire_method);
} }
} }
wsh->wire_reply = GNUNET_JSON_PACK (
wsh->wire_reply = TALER_MHD_MAKE_JSON_PACK (
GNUNET_JSON_pack_array_steal ("accounts", GNUNET_JSON_pack_array_steal ("accounts",
wire_accounts_array), wire_accounts_array),
GNUNET_JSON_pack_object_steal ("fees", GNUNET_JSON_pack_object_steal ("fees",
wire_fee_object), wire_fee_object),
GNUNET_JSON_pack_data_auto ("master_public_key", GNUNET_JSON_pack_data_auto ("master_public_key",
&TEH_master_public_key)); &TEH_master_public_key));
{
char dat[128];
struct GNUNET_TIME_Timestamp m;
m = GNUNET_TIME_absolute_to_timestamp (cache_expiration);
TALER_MHD_get_date_string (m.abs_time,
dat);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Setting 'Expires' header for '/wire' to '%s'\n",
dat);
GNUNET_break (MHD_YES ==
MHD_add_response_header (wsh->wire_reply,
MHD_HTTP_HEADER_EXPIRES,
dat));
}
TALER_MHD_add_global_headers (wsh->wire_reply);
{
struct GNUNET_HashCode h;
char etag[sizeof (h) * 2];
char *end;
GNUNET_CRYPTO_hash_context_finish (hc,
&h);
end = GNUNET_STRINGS_data_to_string (&h,
sizeof (h),
etag,
sizeof (etag));
*end = '\0';
GNUNET_break (MHD_YES ==
MHD_add_response_header (wsh->wire_reply,
MHD_HTTP_HEADER_ETAG,
etag));
}
wsh->http_status = MHD_HTTP_OK; wsh->http_status = MHD_HTTP_OK;
return wsh; return wsh;
} }
@ -509,9 +550,9 @@ TEH_handler_wire (struct TEH_RequestContext *rc,
MHD_HTTP_INTERNAL_SERVER_ERROR, MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
NULL); NULL);
return TALER_MHD_reply_json (rc->connection, return MHD_queue_response (rc->connection,
wsh->wire_reply, wsh->http_status,
wsh->http_status); wsh->wire_reply);
} }

View File

@ -207,6 +207,18 @@ TALER_MHD_reply_with_ec (struct MHD_Connection *connection,
const char *detail); const char *detail);
/**
* Produce HTTP "Date:" header.
*
* @param at time to write to @a date
* @param[out] date where to write the header, with
* at least 128 bytes available space.
*/
void
TALER_MHD_get_date_string (struct GNUNET_TIME_Absolute at,
char date[128]);
/** /**
* Make JSON response object. * Make JSON response object.
* *

View File

@ -178,8 +178,8 @@ TALER_MHD_reply_legal (struct MHD_Connection *conn,
a = GNUNET_TIME_relative_to_absolute (MAX_TERMS_CACHING); a = GNUNET_TIME_relative_to_absolute (MAX_TERMS_CACHING);
m = GNUNET_TIME_absolute_to_timestamp (a); m = GNUNET_TIME_absolute_to_timestamp (a);
get_date_string (m.abs_time, TALER_MHD_get_date_string (m.abs_time,
dat); dat);
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Setting 'Expires' header to '%s'\n", "Setting 'Expires' header to '%s'\n",
dat); dat);

View File

@ -502,4 +502,49 @@ TALER_MHD_reply_static (struct MHD_Connection *connection,
} }
void
TALER_MHD_get_date_string (struct GNUNET_TIME_Absolute at,
char date[128])
{
static const char *const days[] =
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char *const mons[] =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
"Nov", "Dec"};
struct tm now;
time_t t;
#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
! defined(HAVE_GMTIME_R)
struct tm*pNow;
#endif
date[0] = 0;
t = (time_t) (at.abs_value_us / 1000LL / 1000LL);
#if defined(HAVE_C11_GMTIME_S)
if (NULL == gmtime_s (&t, &now))
return;
#elif defined(HAVE_W32_GMTIME_S)
if (0 != gmtime_s (&now, &t))
return;
#elif defined(HAVE_GMTIME_R)
if (NULL == gmtime_r (&t, &now))
return;
#else
pNow = gmtime (&t);
if (NULL == pNow)
return;
now = *pNow;
#endif
sprintf (date,
"%3s, %02u %3s %04u %02u:%02u:%02u GMT",
days[now.tm_wday % 7],
(unsigned int) now.tm_mday,
mons[now.tm_mon % 12],
(unsigned int) (1900 + now.tm_year),
(unsigned int) now.tm_hour,
(unsigned int) now.tm_min,
(unsigned int) now.tm_sec);
}
/* end of mhd_responses.c */ /* end of mhd_responses.c */