Skip to content

Commit 89b48ee

Browse files
committed
fix: Allow concurrent requests
1 parent 542f2b9 commit 89b48ee

File tree

3 files changed

+37
-11
lines changed

3 files changed

+37
-11
lines changed

src/OpenApi/src/Services/Schemas/OpenApiSchemaStore.cs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Concurrent;
45
using System.IO.Pipelines;
56
using System.Text.Json.Nodes;
67
using Microsoft.AspNetCore.Http;
@@ -14,7 +15,7 @@ namespace Microsoft.AspNetCore.OpenApi;
1415
/// </summary>
1516
internal sealed class OpenApiSchemaStore
1617
{
17-
private readonly Dictionary<OpenApiSchemaKey, JsonNode> _schemas = new()
18+
private readonly ConcurrentDictionary<OpenApiSchemaKey, JsonNode> _schemas = new()
1819
{
1920
// Pre-populate OpenAPI schemas for well-defined types in ASP.NET Core.
2021
[new OpenApiSchemaKey(typeof(IFormFile), null)] = new JsonObject
@@ -48,8 +49,8 @@ internal sealed class OpenApiSchemaStore
4849
},
4950
};
5051

51-
public readonly Dictionary<OpenApiSchema, string?> SchemasByReference = new(OpenApiSchemaComparer.Instance);
52-
private readonly Dictionary<string, int> _referenceIdCounter = new();
52+
public readonly ConcurrentDictionary<OpenApiSchema, string?> SchemasByReference = new(OpenApiSchemaComparer.Instance);
53+
private readonly ConcurrentDictionary<string, int> _referenceIdCounter = new();
5354

5455
/// <summary>
5556
/// Resolves the JSON schema for the given type and parameter description.
@@ -59,13 +60,7 @@ internal sealed class OpenApiSchemaStore
5960
/// <returns>A <see cref="JsonObject" /> representing the JSON schema associated with the key.</returns>
6061
public JsonNode GetOrAdd(OpenApiSchemaKey key, Func<OpenApiSchemaKey, JsonNode> valueFactory)
6162
{
62-
if (_schemas.TryGetValue(key, out var schema))
63-
{
64-
return schema;
65-
}
66-
var targetSchema = valueFactory(key);
67-
_schemas.Add(key, targetSchema);
68-
return targetSchema;
63+
return _schemas.GetOrAdd(key, valueFactory(key));
6964
}
7065

7166
/// <summary>

src/OpenApi/src/Transformers/Implementations/OpenApiSchemaReferenceTransformer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Concurrent;
45
using System.Linq;
56
using Microsoft.Extensions.DependencyInjection;
67
using Microsoft.OpenApi.Models;
@@ -85,7 +86,7 @@ public Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerC
8586
/// <param name="schema">The inline schema to replace with a reference.</param>
8687
/// <param name="schemasByReference">A cache of schemas and their associated reference IDs.</param>
8788
/// <param name="isTopLevel">When <see langword="true" />, will skip resolving references for the top-most schema provided.</param>
88-
internal static OpenApiSchema? ResolveReferenceForSchema(OpenApiSchema? schema, Dictionary<OpenApiSchema, string?> schemasByReference, bool isTopLevel = false)
89+
internal static OpenApiSchema? ResolveReferenceForSchema(OpenApiSchema? schema, ConcurrentDictionary<OpenApiSchema, string?> schemasByReference, bool isTopLevel = false)
8990
{
9091
if (schema is null)
9192
{
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Net;
5+
using System.Net.Http;
6+
7+
namespace Microsoft.AspNetCore.OpenApi.Tests.Integration;
8+
9+
public class OpenApiDocumentConcurrentRequestTests(SampleAppFixture fixture) : IClassFixture<SampleAppFixture>
10+
{
11+
[Fact]
12+
public async Task MapOpenApi_HandlesConcurrentRequests()
13+
{
14+
var client = fixture.CreateClient();
15+
Task<HttpResponseMessage>[] requests =
16+
[
17+
client.GetAsync("/openapi/v1.json"),
18+
client.GetAsync("/openapi/v1.json"),
19+
client.GetAsync("/openapi/v1.json")
20+
];
21+
22+
var results = await Task.WhenAll(requests);
23+
24+
foreach (var result in results)
25+
{
26+
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
27+
}
28+
29+
}
30+
}

0 commit comments

Comments
 (0)