@@ -157,7 +157,11 @@ class FilterPersistenceTest : public Event::TestUsingSimulatedTime,
157157 }
158158 });
159159 tls_store_emptied_ = std::make_unique<absl::Notification>();
160- GlobalTlsStores::registerEmptiedCb ([&]() { tls_store_emptied_->Notify (); });
160+ GlobalTlsStores::registerEmptiedCb ([&]() {
161+ if (tls_store_emptied_ != nullptr && !tls_store_emptied_->HasBeenNotified ()) {
162+ tls_store_emptied_->Notify ();
163+ }
164+ });
161165 }
162166
163167 void updateConfigInPlace (std::function<void (envoy::config::bootstrap::v3::Bootstrap&)> modifier) {
@@ -210,7 +214,10 @@ class FilterPersistenceTest : public Event::TestUsingSimulatedTime,
210214 cleanupUpstreamAndDownstream ();
211215 }
212216
213- void TearDown () override { cleanUp (); }
217+ void TearDown () override {
218+ cleanUp ();
219+ GlobalTlsStores::registerEmptiedCb (nullptr );
220+ }
214221
215222 // Send a request through the envoy & possibly to traffic_upstream_. Returns
216223 // the response's status code.
@@ -535,6 +542,86 @@ domain: "test_domain"
535542 ASSERT_EQ (sendRequest (&headers), " 429" );
536543}
537544
545+ // Verify that the callback registered via registerEmptiedCb fires every time
546+ // the stores map becomes empty.
547+ TEST_P (FilterPersistenceTest, TestEmptiedCallbackFiresMultipleTimes) {
548+ uint32_t emptied_count = 0 ;
549+ auto n1 = std::make_shared<absl::Notification>();
550+ GlobalTlsStores::registerEmptiedCb ([&emptied_count, &n1]() {
551+ emptied_count++;
552+ if (!n1->HasBeenNotified ()) {
553+ n1->Notify ();
554+ }
555+ });
556+
557+ auto remove_rlqs = [&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) {
558+ auto * listener = bootstrap.mutable_static_resources ()->mutable_listeners (0 );
559+ auto * hcm_filter = listener->mutable_filter_chains (0 )->mutable_filters (0 );
560+ HttpConnectionManager hcm_config;
561+ hcm_filter->mutable_typed_config ()->UnpackTo (&hcm_config);
562+ auto * filters = hcm_config.mutable_http_filters ();
563+ for (int i = 0 ; i < filters->size ();) {
564+ if (filters->Get (i).name () == " envoy.filters.http.rate_limit_quota" ) {
565+ filters->DeleteSubrange (i, 1 );
566+ } else {
567+ i++;
568+ }
569+ }
570+ hcm_filter->mutable_typed_config ()->PackFrom (hcm_config);
571+ };
572+
573+ // 1. Initial removal of the RLQS filter to trigger the first call of the callback.
574+ updateConfigInPlace (remove_rlqs);
575+ ASSERT_TRUE (n1->WaitForNotificationWithTimeout (absl::Seconds (3 )));
576+ EXPECT_EQ (emptied_count, 1 );
577+
578+ // 2. Add the filter back and verify it works.
579+ updateConfigInPlace ([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) {
580+ auto * listener = bootstrap.mutable_static_resources ()->mutable_listeners (0 );
581+ auto * hcm_filter = listener->mutable_filter_chains (0 )->mutable_filters (0 );
582+ HttpConnectionManager hcm_config;
583+ hcm_filter->mutable_typed_config ()->UnpackTo (&hcm_config);
584+ // Prepend the RLQS filter.
585+ (void )hcm_config.add_http_filters ();
586+ for (int i = hcm_config.http_filters_size () - 1 ; i > 0 ; --i) {
587+ hcm_config.mutable_http_filters ()->SwapElements (i, i - 1 );
588+ }
589+ TestUtility::loadFromYaml (kDefaultRateLimitQuotaFilter , *hcm_config.mutable_http_filters (0 ));
590+ hcm_filter->mutable_typed_config ()->PackFrom (hcm_config);
591+ });
592+ absl::flat_hash_map<std::string, std::string> headers = {{" environment" , " staging" }};
593+ ASSERT_EQ (sendRequest (&headers), " 200" );
594+
595+ // 3. Remove the filter again. The callback should be called a second time.
596+ n1 = std::make_shared<absl::Notification>();
597+ updateConfigInPlace (remove_rlqs);
598+ ASSERT_TRUE (n1->WaitForNotificationWithTimeout (absl::Seconds (3 )));
599+ EXPECT_EQ (emptied_count, 2 );
600+
601+ // Restore the callback for TearDown/subsequent tests.
602+ GlobalTlsStores::registerEmptiedCb ([&]() {
603+ if (tls_store_emptied_ != nullptr && !tls_store_emptied_->HasBeenNotified ()) {
604+ tls_store_emptied_->Notify ();
605+ }
606+ });
607+
608+ // Add it back one last time so TearDown has something to wipe and the
609+ // notification is triggered.
610+ tls_store_emptied_ = std::make_unique<absl::Notification>();
611+ updateConfigInPlace ([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) {
612+ auto * listener = bootstrap.mutable_static_resources ()->mutable_listeners (0 );
613+ auto * hcm_filter = listener->mutable_filter_chains (0 )->mutable_filters (0 );
614+ HttpConnectionManager hcm_config;
615+ hcm_filter->mutable_typed_config ()->UnpackTo (&hcm_config);
616+ (void )hcm_config.add_http_filters ();
617+ for (int i = hcm_config.http_filters_size () - 1 ; i > 0 ; --i) {
618+ hcm_config.mutable_http_filters ()->SwapElements (i, i - 1 );
619+ }
620+ TestUtility::loadFromYaml (kDefaultRateLimitQuotaFilter , *hcm_config.mutable_http_filters (0 ));
621+ hcm_filter->mutable_typed_config ()->PackFrom (hcm_config);
622+ });
623+ }
624+
538625} // namespace
539626} // namespace RateLimitQuota
540627} // namespace HttpFilters
0 commit comments