Skip to content

Commit cda14f3

Browse files
author
Keegan Caruso
committed
- Move CT to property
- Remove LastRefresh
1 parent 4a73dea commit cda14f3

File tree

7 files changed

+95
-52
lines changed

7 files changed

+95
-52
lines changed

src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ public partial class ConfigurationManager<T> : BaseConfigurationManager, IConfig
2626
#pragma warning restore CS0649 // Unused
2727
#pragma warning restore IDE0044 // Add readonly modifier
2828

29-
private CancellationToken _BackgroundTaskCancellationToken = CancellationToken.None;
30-
3129
private DateTime _syncAfter = DateTime.MinValue;
3230
private DateTime SyncAfter
3331
{
@@ -70,6 +68,14 @@ private DateTime LastRequestRefresh
7068
private readonly AutoResetEvent _updateMetadata = new(false);
7169
Task _updateMetadataTask;
7270

71+
/// <summary>
72+
/// Cancellation token to control cancelling the background task.
73+
/// If 'Switch.Microsoft.IdentityModel.UpdateConfigAsBlocking' is set to true,
74+
/// then this will not be used.
75+
/// Note that this does not influence <see cref="GetConfigurationAsync(CancellationToken)"/>.
76+
/// </summary>
77+
public CancellationToken BackgroundTaskCancellationToken { get; set; }
78+
7379
/// <summary>
7480
/// Instantiates a new <see cref="ConfigurationManager{T}"/> that manages automatic and controls refreshing on configuration data.
7581
/// </summary>
@@ -131,7 +137,9 @@ public ConfigurationManager(string metadataAddress, IConfigurationRetriever<T> c
131137
MetadataAddress = metadataAddress;
132138
_docRetriever = docRetriever;
133139
_configRetriever = configRetriever;
134-
EnsureBackgroundTaskIsRunning();
140+
141+
if (!AppContextSwitches.UpdateConfigAsBlocking)
142+
EnsureBackgroundTaskIsRunning();
135143
}
136144

137145
/// <summary>
@@ -297,6 +305,9 @@ private async Task<T> GetConfigurationWithBackgroundTaskUpdatesAsync(Cancellatio
297305

298306
private void EnsureBackgroundTaskIsRunning()
299307
{
308+
if (BackgroundTaskCancellationToken.IsCancellationRequested)
309+
return;
310+
300311
if (_updateMetadataTask == null || _updateMetadataTask.Status != TaskStatus.Running)
301312
_updateMetadataTask = Task.Run(UpdateCurrentConfigurationUsingSignals);
302313
}
@@ -322,14 +333,20 @@ private void TelemetryForUpdate()
322333

323334
private void UpdateCurrentConfigurationUsingSignals()
324335
{
325-
while (!_BackgroundTaskCancellationToken.IsCancellationRequested)
336+
try
326337
{
327-
if (_updateMetadata.WaitOne(500))
338+
while (!BackgroundTaskCancellationToken.IsCancellationRequested)
328339
{
329-
UpdateCurrentConfiguration();
330-
_onBackgroundTaskFinish?.Invoke();
340+
if (_updateMetadata.WaitOne(500))
341+
{
342+
UpdateCurrentConfiguration();
343+
_onBackgroundTaskFinish?.Invoke();
344+
}
331345
}
332346
}
347+
catch (Exception)
348+
{
349+
}
333350
}
334351

335352
/// <summary>

src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager_Blocking.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,6 @@ partial class ConfigurationManager<T> where T : class
1717

1818
private TimeSpan _bootstrapRefreshInterval = TimeSpan.FromSeconds(1);
1919

20-
private DateTime _lastRefresh = DateTime.MinValue;
21-
private DateTime LastRefresh
22-
{
23-
get => _lastRefresh;
24-
set => AtomicUpdateDateTime(ref _lastRefresh, ref value);
25-
}
26-
2720
private async Task<T> GetConfigurationWithBlockingAsync(CancellationToken cancel)
2821
{
2922
Exception _fetchMetadataFailure = null;
@@ -53,7 +46,7 @@ private async Task<T> GetConfigurationWithBlockingAsync(CancellationToken cancel
5346
throw LogHelper.LogExceptionMessage(new InvalidConfigurationException(LogHelper.FormatInvariant(LogMessages.IDX20810, result.ErrorMessage)));
5447
}
5548

56-
LastRefresh = _timeProvider.GetUtcNow().UtcDateTime;
49+
LastRequestRefresh = _timeProvider.GetUtcNow().UtcDateTime;
5750
TelemetryForUpdateBlocking();
5851
UpdateConfiguration(configuration);
5952
}
@@ -129,7 +122,7 @@ private void RequestRefreshBlocking()
129122
{
130123
DateTime now = _timeProvider.GetUtcNow().UtcDateTime;
131124

132-
if (now >= DateTimeUtil.Add(LastRefresh, RefreshInterval) || _isFirstRefreshRequest)
125+
if (now >= DateTimeUtil.Add(LastRequestRefresh, RefreshInterval) || _isFirstRefreshRequest)
133126
{
134127
_refreshRequested = true;
135128
_syncAfter = now;

src/Microsoft.IdentityModel.Protocols/GlobalSuppressions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@
1212
#if NET6_0_OR_GREATER
1313
[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Protocols.AuthenticationProtocolMessage.BuildRedirectUrl~System.String")]
1414
#endif
15+
16+
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types",
17+
Justification = "Background thread needs to never throw an unhandled exception.",
18+
Scope = "member",
19+
Target = "~M:Microsoft.IdentityModel.Protocols.ConfigurationManager`1.UpdateCurrentConfigurationUsingSignals")]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.BackgroundTaskCancellationToken.get -> System.Threading.CancellationToken
2+
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.BackgroundTaskCancellationToken.set -> void

test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/ConfigurationManagerTelemetryTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ private static async Task RequestRefresh_ExpectedTagsBody(bool blocking = false)
5050
TelemetryClient = testTelemetryClient
5151
};
5252

53-
TestUtilities.SetField(configurationManager, "_BackgroundTaskCancellationToken", cts.Token);
53+
configurationManager.BackgroundTaskCancellationToken = cts.Token;
5454

5555
var cancel = new CancellationToken();
5656
AutoResetEvent resetEvent = ConfigurationManagerTests.SetupResetEvent(configurationManager, blocking);
@@ -122,7 +122,7 @@ private static async Task GetConfigurationAsync_ExpectedTagList_Body(
122122
TelemetryClient = testTelemetryClient
123123
};
124124

125-
TestUtilities.SetField(configurationManager, "_BackgroundTaskCancellationToken", cts.Token);
125+
configurationManager.BackgroundTaskCancellationToken = cts.Token;
126126
AutoResetEvent resetEvent = ConfigurationManagerTests.SetupResetEvent(configurationManager, blocking);
127127

128128
var timeProvider = new FakeTimeProvider();

test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/ConfigurationManagerTests.cs

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public async Task GetPublicMetadata(ConfigurationManagerTheoryData<OpenIdConnect
4444
theoryData.DocumentRetriever,
4545
theoryData.ConfigurationValidator);
4646

47-
TestUtilities.SetField(configurationManager, "_BackgroundTaskCancellationToken", cts.Token);
47+
configurationManager.BackgroundTaskCancellationToken = cts.Token;
4848

4949
var configuration = await configurationManager.GetConfigurationAsync(CancellationToken.None);
5050

@@ -203,7 +203,7 @@ private async Task FetchMetadataFailureTestBody()
203203

204204
var documentRetriever = new HttpDocumentRetriever(HttpResponseMessageUtils.SetupHttpClientThatReturns("OpenIdConnectMetadata.json", HttpStatusCode.NotFound));
205205
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>("OpenIdConnectMetadata.json", new OpenIdConnectConfigurationRetriever(), documentRetriever);
206-
TestUtilities.SetField(configManager, "_BackgroundTaskCancellationToken", cts.Token);
206+
configManager.BackgroundTaskCancellationToken = cts.Token;
207207

208208
// First time to fetch metadata
209209
try
@@ -252,7 +252,7 @@ public async Task VerifyInterlockGuardForGetConfigurationAsync()
252252
new OpenIdConnectConfigurationRetriever(),
253253
inMemoryDocumentRetriever);
254254

255-
TestUtilities.SetField(configurationManager, "_BackgroundTaskCancellationToken", cts.Token);
255+
configurationManager.BackgroundTaskCancellationToken = cts.Token;
256256

257257
OpenIdConnectConfiguration configuration = await configurationManager.GetConfigurationAsync();
258258

@@ -305,7 +305,7 @@ public async Task BootstrapRefreshIntervalTest()
305305
documentRetriever)
306306
{ RefreshInterval = TimeSpan.FromSeconds(2) };
307307

308-
TestUtilities.SetField(configManager, "_BackgroundTaskCancellationToken", cts.Token);
308+
configManager.BackgroundTaskCancellationToken = cts.Token;
309309

310310
// ConfigurationManager._syncAfter is set to DateTimeOffset.MinValue on startup
311311
// If obtaining the metadata fails due to error, the value should not change
@@ -406,7 +406,7 @@ public void GetSets()
406406

407407
TestUtilities.WriteHeader($"{this}.GetSets", "GetSets", true);
408408

409-
int ExpectedPropertyCount = 7;
409+
int ExpectedPropertyCount = 8;
410410
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>("OpenIdConnectMetadata.json", new OpenIdConnectConfigurationRetriever(), new FileDocumentRetriever());
411411
Type type = typeof(ConfigurationManager<OpenIdConnectConfiguration>);
412412
PropertyInfo[] properties = type.GetProperties();
@@ -460,7 +460,6 @@ private async Task AutomaticRefreshIntervalBody(ConfigurationManagerTheoryData<O
460460

461461
theoryData.ConfigurationManager.MetadataAddress = theoryData.UpdatedMetadataAddress;
462462
TestUtilities.SetField(theoryData.ConfigurationManager, "_syncAfter", theoryData.SyncAfter.UtcDateTime);
463-
TestUtilities.SetField(theoryData.ConfigurationManager, "_BackgroundTaskCancellationToken", theoryData.CancellationTokenSource.Token);
464463
var updatedConfiguration = await theoryData.ConfigurationManager.GetConfigurationAsync(CancellationToken.None);
465464

466465
if (!blocking && theoryData.SyncAfter < DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(1)))
@@ -503,42 +502,57 @@ public static TheoryData<ConfigurationManagerTheoryData<OpenIdConnectConfigurati
503502
{
504503
var theoryData = new TheoryData<ConfigurationManagerTheoryData<OpenIdConnectConfiguration>>();
505504

505+
var cts = new CancellationTokenSource();
506+
506507
// Failing to get metadata returns existing.
507508
theoryData.Add(new ConfigurationManagerTheoryData<OpenIdConnectConfiguration>("HttpFault_ReturnExisting")
508509
{
509510
ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
510511
"AADCommonV1Json",
511512
new OpenIdConnectConfigurationRetriever(),
512-
InMemoryDocumentRetriever),
513-
CancellationTokenSource = new(),
513+
InMemoryDocumentRetriever)
514+
{
515+
BackgroundTaskCancellationToken = cts.Token
516+
},
517+
CancellationTokenSource = cts,
514518
ExpectedConfiguration = OpenIdConfigData.AADCommonV1Config,
515519
ExpectedUpdatedConfiguration = OpenIdConfigData.AADCommonV1Config,
516520
SyncAfter = DateTime.UtcNow - TimeSpan.FromDays(2),
517521
UpdatedMetadataAddress = "https://httpstat.us/429"
518522
});
519523

524+
cts = new();
525+
520526
// AutomaticRefreshInterval interval should return same config.
521527
theoryData.Add(new ConfigurationManagerTheoryData<OpenIdConnectConfiguration>("AutomaticRefreshIntervalNotHit")
522528
{
523529
ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
524530
"AADCommonV1Json",
525531
new OpenIdConnectConfigurationRetriever(),
526-
InMemoryDocumentRetriever),
527-
CancellationTokenSource = new(),
532+
InMemoryDocumentRetriever)
533+
{
534+
BackgroundTaskCancellationToken = cts.Token
535+
},
536+
CancellationTokenSource = cts,
528537
ExpectedConfiguration = OpenIdConfigData.AADCommonV1Config,
529538
ExpectedUpdatedConfiguration = OpenIdConfigData.AADCommonV1Config,
530539
SyncAfter = DateTime.UtcNow + TimeSpan.FromDays(2),
531540
UpdatedMetadataAddress = "AADCommonV2Json"
532541
});
533542

543+
cts = new();
544+
534545
// AutomaticRefreshInterval should pick up new bits.
535546
theoryData.Add(new ConfigurationManagerTheoryData<OpenIdConnectConfiguration>("AutomaticRefreshIntervalHit")
536547
{
537548
ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
538549
"AADCommonV1Json",
539550
new OpenIdConnectConfigurationRetriever(),
540-
InMemoryDocumentRetriever),
541-
CancellationTokenSource = new(),
551+
InMemoryDocumentRetriever)
552+
{
553+
BackgroundTaskCancellationToken = cts.Token
554+
},
555+
CancellationTokenSource = cts,
542556
ExpectedConfiguration = OpenIdConfigData.AADCommonV1Config,
543557
ExpectedUpdatedConfiguration = OpenIdConfigData.AADCommonV2Config,
544558
SyncAfter = DateTime.UtcNow,
@@ -573,7 +587,6 @@ private async Task RequestRefreshBody(ConfigurationManagerTheoryData<OpenIdConne
573587

574588
var timeProvider = new FakeTimeProvider();
575589
TestUtilities.SetField(theoryData.ConfigurationManager, "_timeProvider", timeProvider);
576-
TestUtilities.SetField(theoryData.ConfigurationManager, "_BackgroundTaskCancellationToken", theoryData.CancellationTokenSource.Token);
577590

578591
// the first call to RequestRefresh will trigger a refresh with ConfigurationManager.RefreshInterval being ignored.
579592
// Testing RefreshInterval requires a two calls, the second call will trigger a refresh with ConfigurationManager.RefreshInterval being used.
@@ -611,14 +624,17 @@ public static TheoryData<ConfigurationManagerTheoryData<OpenIdConnectConfigurati
611624
{
612625
var theoryData = new TheoryData<ConfigurationManagerTheoryData<OpenIdConnectConfiguration>>();
613626

614-
// RefreshInterval set to 1 sec should return new config.
627+
var cts = new CancellationTokenSource();
615628
theoryData.Add(new ConfigurationManagerTheoryData<OpenIdConnectConfiguration>("RequestRefresh_TimeSpan_1000ms")
616629
{
617630
ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
618631
"AADCommonV1Json",
619632
new OpenIdConnectConfigurationRetriever(),
620-
InMemoryDocumentRetriever),
621-
CancellationTokenSource = new(),
633+
InMemoryDocumentRetriever)
634+
{
635+
BackgroundTaskCancellationToken = cts.Token
636+
},
637+
CancellationTokenSource = cts,
622638
ExpectedConfiguration = OpenIdConfigData.AADCommonV1Config,
623639
ExpectedUpdatedConfiguration = OpenIdConfigData.AADCommonV2Config,
624640
RefreshInterval = TimeSpan.FromSeconds(1),
@@ -627,14 +643,17 @@ public static TheoryData<ConfigurationManagerTheoryData<OpenIdConnectConfigurati
627643
UpdatedMetadataAddress = "AADCommonV2Json"
628644
});
629645

630-
// RefreshInterval set to TimeSpan.MaxValue should return same config.
646+
cts = new CancellationTokenSource();
631647
theoryData.Add(new ConfigurationManagerTheoryData<OpenIdConnectConfiguration>("RequestRefresh_TimeSpan_MaxValue")
632648
{
633649
ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
634650
"AADCommonV1Json",
635651
new OpenIdConnectConfigurationRetriever(),
636-
InMemoryDocumentRetriever),
637-
CancellationTokenSource = new(),
652+
InMemoryDocumentRetriever)
653+
{
654+
BackgroundTaskCancellationToken = cts.Token
655+
},
656+
CancellationTokenSource = cts,
638657
ExpectedConfiguration = OpenIdConfigData.AADCommonV1Config,
639658
ExpectedUpdatedConfiguration = OpenIdConfigData.AADCommonV1Config,
640659
RefreshInterval = TimeSpan.MaxValue,
@@ -643,14 +662,17 @@ public static TheoryData<ConfigurationManagerTheoryData<OpenIdConnectConfigurati
643662
UpdatedMetadataAddress = "AADCommonV2Json"
644663
});
645664

646-
// First RequestRefresh should pickup new config
665+
cts = new CancellationTokenSource();
647666
theoryData.Add(new ConfigurationManagerTheoryData<OpenIdConnectConfiguration>("RequestRefresh_FirstRefresh")
648667
{
649668
ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
650669
"AADCommonV1Json",
651670
new OpenIdConnectConfigurationRetriever(),
652-
InMemoryDocumentRetriever),
653-
CancellationTokenSource = new(),
671+
InMemoryDocumentRetriever)
672+
{
673+
BackgroundTaskCancellationToken = cts.Token
674+
},
675+
CancellationTokenSource = cts,
654676
ExpectedConfiguration = OpenIdConfigData.AADCommonV1Config,
655677
ExpectedUpdatedConfiguration = OpenIdConfigData.AADCommonV2Config,
656678
SleepTimeInMs = 1000,
@@ -751,7 +773,7 @@ private async Task CheckSyncAfterBody(bool blocking = false)
751773
new OpenIdConnectConfigurationRetriever(),
752774
docRetriever);
753775

754-
TestUtilities.SetField(configManager, "_BackgroundTaskCancellationToken", cts.Token);
776+
configManager.BackgroundTaskCancellationToken = cts.Token;
755777

756778
AutoResetEvent resetEvent = SetupResetEvent(configManager, blocking);
757779

@@ -833,7 +855,7 @@ private async Task GetConfigurationBody()
833855
new OpenIdConnectConfigurationRetriever(),
834856
docRetriever);
835857

836-
TestUtilities.SetField(configManager, "_BackgroundTaskCancellationToken", cts.Token);
858+
configManager.BackgroundTaskCancellationToken = cts.Token;
837859

838860
var configuration = await configManager.GetConfigurationAsync(CancellationToken.None);
839861

@@ -974,7 +996,7 @@ private async Task RequestRefresh_RespectsRefreshInterval_Body(bool blocking = f
974996
docRetriever);
975997

976998
TestUtilities.SetField(configManager, "_timeProvider", timeProvider);
977-
TestUtilities.SetField(configManager, "_BackgroundTaskCancellationToken", cts.Token);
999+
configManager.BackgroundTaskCancellationToken = cts.Token;
9781000

9791001
var resetEvent = SetupResetEvent(configManager, blocking);
9801002

@@ -1071,7 +1093,7 @@ private async Task GetConfigurationAsync_RespectsRefreshIntervalBody(bool blocki
10711093
docRetriever);
10721094

10731095
TestUtilities.SetField(configManager, "_timeProvider", timeProvider);
1074-
TestUtilities.SetField(configManager, "_BackgroundTaskCancellationToken", cts.Token);
1096+
configManager.BackgroundTaskCancellationToken = cts.Token;
10751097

10761098
TimeSpan advanceInterval = BaseConfigurationManager.DefaultAutomaticRefreshInterval.Add(TimeSpan.FromSeconds(configManager.AutomaticRefreshInterval.TotalSeconds));
10771099

@@ -1138,9 +1160,10 @@ private async Task ValidateOIDCConfigurationBody(ConfigurationManagerTheoryData<
11381160
theoryData.MetadataAddress,
11391161
theoryData.ConfigurationRetriever,
11401162
theoryData.DocumentRetriever,
1141-
theoryData.ConfigurationValidator);
1142-
1143-
TestUtilities.SetField(configurationManager, "_BackgroundTaskCancellationToken", theoryData.CancellationTokenSource.Token);
1163+
theoryData.ConfigurationValidator)
1164+
{
1165+
BackgroundTaskCancellationToken = theoryData.CancellationTokenSource.Token
1166+
};
11441167

11451168
var resetEvent = SetupResetEvent(configurationManager, blocking);
11461169

0 commit comments

Comments
 (0)