Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit a097f3c

Browse files
Merge pull request #29 from profet23/dev
Proposed solution for bug #26
2 parents 3a67512 + 4282f37 commit a097f3c

File tree

3 files changed

+65
-4
lines changed

3 files changed

+65
-4
lines changed

src/IdentityModel.AspNetCore.OAuth2Introspection/Infrastructure/AsyncLazy.cs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,42 @@
66

77
namespace IdentityModel.AspNetCore.OAuth2Introspection.Infrastructure
88
{
9-
internal class AsyncLazy<T> : Lazy<Task<T>>
9+
internal sealed class AsyncLazy<T>
1010
{
11-
public AsyncLazy(Func<Task<T>> taskFactory) :
12-
base(() => Task.Factory.StartNew(taskFactory).Unwrap())
13-
{ }
11+
private Lazy<Task<T>> _lazyTaskFactory;
12+
private readonly Func<Task<T>> _taskFactory;
13+
private readonly object _lazyInitializationGuard = new object();
14+
15+
public AsyncLazy(Func<Task<T>> taskFactory)
16+
{
17+
_taskFactory = taskFactory;
18+
_lazyTaskFactory = InitLazy(_taskFactory);
19+
}
20+
21+
public Task<T> Value
22+
{
23+
get
24+
{
25+
//If the lazy value is not yet created, we should just return the lazy value (which will create it)
26+
//If the value has been created and the value (which is a Task<T>) is not faulted, we should just return the value;
27+
if (!(_lazyTaskFactory.IsValueCreated && _lazyTaskFactory.Value.IsFaulted))
28+
return _lazyTaskFactory.Value;
29+
30+
lock (_lazyInitializationGuard)
31+
{
32+
if (_lazyTaskFactory.IsValueCreated && _lazyTaskFactory.Value.IsFaulted)
33+
{
34+
_lazyTaskFactory = InitLazy(_taskFactory);
35+
}
36+
37+
return _lazyTaskFactory.Value;
38+
}
39+
}
40+
}
41+
42+
private static Lazy<Task<T>> InitLazy(Func<Task<T>> taskFactory)
43+
{
44+
return new Lazy<Task<T>>(() => Task.Run(taskFactory));
45+
}
1446
}
1547
}

test/Tests/Introspection.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,5 +164,27 @@ public async Task ActiveToken_With_SavedToken_And_Caching()
164164

165165
responseData.Should().Contain("token", expectedToken);
166166
}
167+
168+
[Fact]
169+
public async Task ActiveToken_With_Discovery_Unavailable_On_First_Request()
170+
{
171+
var handler = new DiscoveryEndpointHandler();
172+
173+
var client = PipelineFactory.CreateClient((o) =>
174+
{
175+
_options(o);
176+
o.DiscoveryHttpHandler = handler;
177+
o.IntrospectionHttpHandler = new IntrospectionEndpointHandler(IntrospectionEndpointHandler.Behavior.Active);
178+
});
179+
180+
client.SetBearerToken("sometoken");
181+
182+
handler.IsFailureTest = true;
183+
await Assert.ThrowsAsync<InvalidOperationException>(async () => await client.GetAsync("http://test"));
184+
185+
handler.IsFailureTest = false;
186+
var result = await client.GetAsync("http://test");
187+
result.StatusCode.Should().Be(HttpStatusCode.OK);
188+
}
167189
}
168190
}

test/Tests/Util/DiscoveryEndpointHandler.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,15 @@ class DiscoveryEndpointHandler : HttpMessageHandler
1313
{
1414
public string Endpoint { get; set; }
1515

16+
public bool IsFailureTest { get; set; } = false;
17+
1618
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
1719
{
20+
if (IsFailureTest)
21+
{
22+
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
23+
}
24+
1825
if (request.RequestUri.AbsoluteUri.ToString() == "https://authority.com/.well-known/openid-configuration")
1926
{
2027
Endpoint = request.RequestUri.AbsoluteUri;

0 commit comments

Comments
 (0)