3737#include < openssl/sha.h>
3838#include < openssl/hmac.h>
3939
40+ #include < chrono>
41+ #include < atomic>
42+ #include < mutex>
43+ #include < shared_mutex>
44+
4045#include < ts/ts.h>
4146#include < ts/remap.h>
4247#include " tscore/ink_config.h"
@@ -153,6 +158,7 @@ ConfigCache gConfCache;
153158// One configuration setup
154159//
155160int event_handler (TSCont, TSEvent, void *); // Forward declaration
161+ int config_reloader (TSCont, TSEvent, void *);
156162
157163class S3Config
158164{
@@ -162,6 +168,9 @@ class S3Config
162168 if (get_cont) {
163169 _cont = TSContCreate (event_handler, nullptr );
164170 TSContDataSet (_cont, static_cast <void *>(this ));
171+
172+ _conf_rld = TSContCreate (config_reloader, TSMutexCreate ());
173+ TSContDataSet (_conf_rld, static_cast <void *>(this ));
165174 }
166175 }
167176
@@ -171,9 +180,13 @@ class S3Config
171180 TSfree (_secret);
172181 TSfree (_keyid);
173182 TSfree (_token);
183+ TSfree (_conf_fname);
174184 if (_cont) {
175185 TSContDestroy (_cont);
176186 }
187+ if (_conf_rld) {
188+ TSContDestroy (_conf_rld);
189+ }
177190 }
178191
179192 // Is this configuration usable?
@@ -212,16 +225,19 @@ class S3Config
212225 copy_changes_from (const S3Config *src)
213226 {
214227 if (src->_secret ) {
228+ TSfree (_secret);
215229 _secret = TSstrdup (src->_secret );
216230 _secret_len = src->_secret_len ;
217231 }
218232
219233 if (src->_keyid ) {
234+ TSfree (_keyid);
220235 _keyid = TSstrdup (src->_keyid );
221236 _keyid_len = src->_keyid_len ;
222237 }
223238
224239 if (src->_token ) {
240+ TSfree (_token);
225241 _token = TSstrdup (src->_token );
226242 _token_len = src->_token_len ;
227243 }
@@ -250,6 +266,13 @@ class S3Config
250266 _region_map = src->_region_map ;
251267 _region_map_modified = true ;
252268 }
269+
270+ _expiration = src->_expiration ;
271+
272+ if (src->_conf_fname ) {
273+ TSfree (_conf_fname);
274+ _conf_fname = TSstrdup (src->_conf_fname );
275+ }
253276 }
254277
255278 // Getters
@@ -319,6 +342,24 @@ class S3Config
319342 return _region_map;
320343 }
321344
345+ long
346+ expiration () const
347+ {
348+ return _expiration;
349+ }
350+
351+ const char *
352+ conf_fname () const
353+ {
354+ return _conf_fname;
355+ }
356+
357+ int
358+ incr_conf_reload_count ()
359+ {
360+ return _conf_reload_count++;
361+ }
362+
322363 // Setters
323364 void
324365 set_secret (const char *s)
@@ -380,6 +421,25 @@ class S3Config
380421 _region_map_modified = true ;
381422 }
382423
424+ void
425+ set_expiration (const char *s)
426+ {
427+ _expiration = strtol (s, nullptr , 10 );
428+ }
429+
430+ void
431+ set_conf_fname (const char *s)
432+ {
433+ TSfree (_conf_fname);
434+ _conf_fname = TSstrdup (s);
435+ }
436+
437+ void
438+ reset_conf_reload_count ()
439+ {
440+ _conf_reload_count = 0 ;
441+ }
442+
383443 // Parse configs from an external file
384444 bool parse_config (const std::string &filename);
385445
@@ -391,6 +451,15 @@ class S3Config
391451 TSHttpTxnHookAdd (txnp, TS_HTTP_SEND_REQUEST_HDR_HOOK, _cont);
392452 }
393453
454+ void
455+ schedule_conf_reload (long delay) const
456+ {
457+ TSContScheduleOnPool (_conf_rld, delay * 1000 , TS_THREAD_POOL_NET);
458+ }
459+
460+ std::shared_mutex reload_mutex;
461+ std::atomic_bool reload_waiting = false ;
462+
394463private:
395464 char *_secret = nullptr ;
396465 size_t _secret_len = 0 ;
@@ -403,12 +472,16 @@ class S3Config
403472 bool _version_modified = false ;
404473 bool _virt_host_modified = false ;
405474 TSCont _cont = nullptr ;
475+ TSCont _conf_rld = nullptr ;
406476 StringSet _v4includeHeaders;
407477 bool _v4includeHeaders_modified = false ;
408478 StringSet _v4excludeHeaders;
409479 bool _v4excludeHeaders_modified = false ;
410480 StringMap _region_map;
411481 bool _region_map_modified = false ;
482+ long _expiration = 0 ;
483+ char *_conf_fname = nullptr ;
484+ int _conf_reload_count = 0 ;
412485};
413486
414487bool
@@ -465,6 +538,8 @@ S3Config::parse_config(const std::string &config_fname)
465538 set_exclude_headers (pos2 + 19 );
466539 } else if (0 == strncasecmp (pos2, " v4-region-map=" , 14 )) {
467540 set_region_map (pos2 + 14 );
541+ } else if (0 == strncasecmp (pos2, " expiration=" , 11 )) {
542+ set_expiration (pos2 + 11 );
468543 } else {
469544 // ToDo: warnings?
470545 }
@@ -499,6 +574,7 @@ ConfigCache::get(const char *fname)
499574 if (tv.tv_sec > (it->second .second + _ttl)) {
500575 // Update the cached configuration file.
501576 S3Config *s3 = new S3Config (false ); // false == this config does not get the continuation
577+ s3->set_conf_fname (fname);
502578
503579 TSDebug (PLUGIN_NAME, " Configuration from %s is stale, reloading" , config_fname.c_str ());
504580 it->second .second = tv.tv_sec ;
@@ -516,6 +592,7 @@ ConfigCache::get(const char *fname)
516592 } else {
517593 // Create a new cached file.
518594 S3Config *s3 = new S3Config (false ); // false == this config does not get the continuation
595+ s3->set_conf_fname (fname);
519596
520597 if (s3->parse_config (config_fname)) {
521598 _cache[config_fname] = std::make_pair (s3, tv.tv_sec );
@@ -876,7 +953,13 @@ event_handler(TSCont cont, TSEvent event, void *edata)
876953 switch (event) {
877954 case TS_EVENT_HTTP_SEND_REQUEST_HDR:
878955 if (request.initialize ()) {
879- status = request.authorize (s3);
956+ while (true ) {
957+ if (!s3->reload_waiting ) {
958+ std::shared_lock lock (s3->reload_mutex );
959+ status = request.authorize (s3);
960+ break ;
961+ }
962+ }
880963 }
881964
882965 if (TS_HTTP_STATUS_OK == status) {
@@ -897,6 +980,63 @@ event_handler(TSCont cont, TSEvent event, void *edata)
897980 return 0 ;
898981}
899982
983+ // If the token has more than one hour to expire, reload is scheduled one hour before expiration.
984+ // If the token has less than one hour to expire, reload is scheduled 15 minutes before expiration.
985+ // If the token has less than 15 minutes to expire, reload is scheduled at the expiration time.
986+ static long
987+ cal_reload_delay (long time_diff)
988+ {
989+ if (time_diff > 3600 ) {
990+ return time_diff - 3600 ;
991+ } else if (time_diff > 900 ) {
992+ return time_diff - 900 ;
993+ } else {
994+ return time_diff;
995+ }
996+ }
997+
998+ int
999+ config_reloader (TSCont cont, TSEvent event, void *edata)
1000+ {
1001+ TSDebug (PLUGIN_NAME, " reloading configs" );
1002+ S3Config *s3 = static_cast <S3Config *>(TSContDataGet (cont));
1003+ S3Config *file_config = gConfCache .get (s3->conf_fname ());
1004+
1005+ if (!file_config->valid ()) {
1006+ TSError (" [%s] requires both shared and AWS secret configuration" , PLUGIN_NAME);
1007+ return TS_ERROR;
1008+ }
1009+
1010+ s3->reload_waiting = true ;
1011+ {
1012+ std::unique_lock lock (s3->reload_mutex );
1013+ s3->copy_changes_from (file_config);
1014+ }
1015+ s3->reload_waiting = false ;
1016+
1017+ if (s3->expiration () == 0 ) {
1018+ TSDebug (PLUGIN_NAME, " disabling auto config reload" );
1019+ } else {
1020+ // auto reload is scheduled to be 5 minutes before the expiration time to get some headroom
1021+ long time_diff = s3->expiration () -
1022+ std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now ().time_since_epoch ()).count ();
1023+ if (time_diff > 0 ) {
1024+ long delay = cal_reload_delay (time_diff);
1025+ TSDebug (PLUGIN_NAME, " scheduling config reload with %ld seconds delay" , delay);
1026+ s3->reset_conf_reload_count ();
1027+ s3->schedule_conf_reload (delay);
1028+ } else {
1029+ TSDebug (PLUGIN_NAME, " config expiration time is in the past, re-checking in 1 minute" );
1030+ if (s3->incr_conf_reload_count () == 10 ) {
1031+ TSError (" [%s] tried to reload config automatically but failed, please try manual reloading the config" , PLUGIN_NAME);
1032+ }
1033+ s3->schedule_conf_reload (60 );
1034+ }
1035+ }
1036+
1037+ return TS_SUCCESS;
1038+ }
1039+
9001040// /////////////////////////////////////////////////////////////////////////////
9011041// Initialize the plugin.
9021042//
@@ -1000,6 +1140,23 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf ATS_UNUSE
10001140 return TS_ERROR;
10011141 }
10021142
1143+ if (s3->expiration () == 0 ) {
1144+ TSDebug (PLUGIN_NAME, " disabling auto config reload" );
1145+ } else {
1146+ // auto reload is scheduled to be 5 minutes before the expiration time to get some headroom
1147+ long time_diff = s3->expiration () -
1148+ std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now ().time_since_epoch ()).count ();
1149+ if (time_diff > 0 ) {
1150+ long delay = cal_reload_delay (time_diff);
1151+ TSDebug (PLUGIN_NAME, " scheduling config reload with %ld seconds delay" , delay);
1152+ s3->reset_conf_reload_count ();
1153+ s3->schedule_conf_reload (delay);
1154+ } else {
1155+ TSDebug (PLUGIN_NAME, " config expiration time is in the past, re-checking in 1 minute" );
1156+ s3->schedule_conf_reload (60 );
1157+ }
1158+ }
1159+
10031160 *ih = static_cast <void *>(s3);
10041161 TSDebug (PLUGIN_NAME, " New rule: access_key=%s, virtual_host=%s, version=%d" , s3->keyid (), s3->virt_host () ? " yes" : " no" ,
10051162 s3->version ());
0 commit comments