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,18 @@ 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+
322357 // Setters
323358 void
324359 set_secret (const char *s)
@@ -380,6 +415,19 @@ class S3Config
380415 _region_map_modified = true ;
381416 }
382417
418+ void
419+ set_expiration (const char *s)
420+ {
421+ _expiration = strtol (s, nullptr , 10 );
422+ }
423+
424+ void
425+ set_conf_fname (const char *s)
426+ {
427+ TSfree (_conf_fname);
428+ _conf_fname = TSstrdup (s);
429+ }
430+
383431 // Parse configs from an external file
384432 bool parse_config (const std::string &filename);
385433
@@ -391,6 +439,15 @@ class S3Config
391439 TSHttpTxnHookAdd (txnp, TS_HTTP_SEND_REQUEST_HDR_HOOK, _cont);
392440 }
393441
442+ void
443+ schedule_conf_reload (long delay) const
444+ {
445+ TSContScheduleOnPool (_conf_rld, delay * 1000 , TS_THREAD_POOL_NET);
446+ }
447+
448+ std::shared_mutex reload_mutex;
449+ std::atomic_bool reload_waiting = false ;
450+
394451private:
395452 char *_secret = nullptr ;
396453 size_t _secret_len = 0 ;
@@ -403,12 +460,15 @@ class S3Config
403460 bool _version_modified = false ;
404461 bool _virt_host_modified = false ;
405462 TSCont _cont = nullptr ;
463+ TSCont _conf_rld = nullptr ;
406464 StringSet _v4includeHeaders;
407465 bool _v4includeHeaders_modified = false ;
408466 StringSet _v4excludeHeaders;
409467 bool _v4excludeHeaders_modified = false ;
410468 StringMap _region_map;
411469 bool _region_map_modified = false ;
470+ long _expiration = 0 ;
471+ char *_conf_fname = nullptr ;
412472};
413473
414474bool
@@ -465,6 +525,8 @@ S3Config::parse_config(const std::string &config_fname)
465525 set_exclude_headers (pos2 + 19 );
466526 } else if (0 == strncasecmp (pos2, " v4-region-map=" , 14 )) {
467527 set_region_map (pos2 + 14 );
528+ } else if (0 == strncasecmp (pos2, " expiration=" , 11 )) {
529+ set_expiration (pos2 + 11 );
468530 } else {
469531 // ToDo: warnings?
470532 }
@@ -499,6 +561,7 @@ ConfigCache::get(const char *fname)
499561 if (tv.tv_sec > (it->second .second + _ttl)) {
500562 // Update the cached configuration file.
501563 S3Config *s3 = new S3Config (false ); // false == this config does not get the continuation
564+ s3->set_conf_fname (fname);
502565
503566 TSDebug (PLUGIN_NAME, " Configuration from %s is stale, reloading" , config_fname.c_str ());
504567 it->second .second = tv.tv_sec ;
@@ -516,6 +579,7 @@ ConfigCache::get(const char *fname)
516579 } else {
517580 // Create a new cached file.
518581 S3Config *s3 = new S3Config (false ); // false == this config does not get the continuation
582+ s3->set_conf_fname (fname);
519583
520584 if (s3->parse_config (config_fname)) {
521585 _cache[config_fname] = std::make_pair (s3, tv.tv_sec );
@@ -876,7 +940,13 @@ event_handler(TSCont cont, TSEvent event, void *edata)
876940 switch (event) {
877941 case TS_EVENT_HTTP_SEND_REQUEST_HDR:
878942 if (request.initialize ()) {
879- status = request.authorize (s3);
943+ while (true ) {
944+ if (!s3->reload_waiting ) {
945+ std::shared_lock lock (s3->reload_mutex );
946+ status = request.authorize (s3);
947+ break ;
948+ }
949+ }
880950 }
881951
882952 if (TS_HTTP_STATUS_OK == status) {
@@ -897,6 +967,42 @@ event_handler(TSCont cont, TSEvent event, void *edata)
897967 return 0 ;
898968}
899969
970+ int
971+ config_reloader (TSCont cont, TSEvent event, void *edata)
972+ {
973+ TSDebug (PLUGIN_NAME, " reloading configs" );
974+ S3Config *s3 = static_cast <S3Config *>(TSContDataGet (cont));
975+ S3Config *file_config = gConfCache .get (s3->conf_fname ());
976+
977+ if (!file_config->valid ()) {
978+ TSError (" [%s] requires both shared and AWS secret configuration" , PLUGIN_NAME);
979+ return TS_ERROR;
980+ }
981+
982+ s3->reload_waiting = true ;
983+ {
984+ std::unique_lock lock (s3->reload_mutex );
985+ s3->copy_changes_from (file_config);
986+ }
987+ s3->reload_waiting = false ;
988+
989+ if (s3->expiration () == 0 ) {
990+ TSDebug (PLUGIN_NAME, " disabling auto config reload" );
991+ } else {
992+ long delay = s3->expiration () -
993+ std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now ().time_since_epoch ()).count ();
994+ if (delay > 0 ) {
995+ TSDebug (PLUGIN_NAME, " scheduling config reload with %ld seconds delay" , delay);
996+ s3->schedule_conf_reload (delay);
997+ } else {
998+ TSDebug (PLUGIN_NAME, " expiration time is in the past, re-checking in 10 seconds" );
999+ s3->schedule_conf_reload (10 );
1000+ }
1001+ }
1002+
1003+ return TS_SUCCESS;
1004+ }
1005+
9001006// /////////////////////////////////////////////////////////////////////////////
9011007// Initialize the plugin.
9021008//
@@ -1000,6 +1106,20 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf ATS_UNUSE
10001106 return TS_ERROR;
10011107 }
10021108
1109+ if (s3->expiration () == 0 ) {
1110+ TSDebug (PLUGIN_NAME, " disabling auto config reload" );
1111+ } else {
1112+ long delay = s3->expiration () -
1113+ std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now ().time_since_epoch ()).count ();
1114+ if (delay > 0 ) {
1115+ TSDebug (PLUGIN_NAME, " scheduling config reload with %ld seconds delay" , delay);
1116+ s3->schedule_conf_reload (delay);
1117+ } else {
1118+ TSDebug (PLUGIN_NAME, " expiration time is in the past, re-checking in 10 seconds" );
1119+ s3->schedule_conf_reload (10 );
1120+ }
1121+ }
1122+
10031123 *ih = static_cast <void *>(s3);
10041124 TSDebug (PLUGIN_NAME, " New rule: access_key=%s, virtual_host=%s, version=%d" , s3->keyid (), s3->virt_host () ? " yes" : " no" ,
10051125 s3->version ());
0 commit comments