Skip to content

Commit

Permalink
chore: Lazy load singleton HttpClient.
Browse files Browse the repository at this point in the history
  • Loading branch information
peombwa committed Oct 17, 2023
1 parent 98ef464 commit 4b71724
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 40 deletions.
31 changes: 18 additions & 13 deletions src/lib/Helpers/ParsingHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ namespace Microsoft.OpenApi.ApiManifest.Helpers;

internal static class ParsingHelpers
{
private static readonly Lazy<HttpClient> s_httpClient;

static ParsingHelpers()
{
s_httpClient = new(() => new HttpClient(new HttpClientHandler()
{
SslProtocols = System.Security.Authentication.SslProtocols.Tls12,
}))
{
Value = { DefaultRequestVersion = HttpVersion.Version20 }
};
}

internal static void ParseMap<T>(JsonElement node, T permissionsDocument, FixedFieldMap<T> handlers)
{
foreach (var element in node.EnumerateObject())
Expand Down Expand Up @@ -141,7 +154,7 @@ internal static IEnumerable<KeyValuePair<string, string>> ParseKey(string key)

internal static async Task<ReadResult> ParseOpenApiAsync(Uri openApiFileUri, bool inlineExternal, CancellationToken cancellationToken)
{
Stream stream = await GetStreamAsync(openApiFileUri, cancellationToken: cancellationToken);
await using var stream = await GetStreamAsync(openApiFileUri, cancellationToken: cancellationToken).ConfigureAwait(false);
return await ParseOpenApiAsync(stream, openApiFileUri, inlineExternal, cancellationToken);
}

Expand All @@ -152,26 +165,18 @@ internal static async Task<ReadResult> ParseOpenApiAsync(Stream stream, Uri open
LoadExternalRefs = inlineExternal,
BaseUrl = openApiFileUri
}
).ReadAsync(stream, cancellationToken);
).ReadAsync(stream, cancellationToken).ConfigureAwait(false);

return result;
}

internal static async Task<Stream> GetStreamAsync(Uri uri, HttpMessageHandler? finalHandler = null, CancellationToken cancellationToken = default)
internal static async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken = default)
{
if (!uri.Scheme.StartsWith("http"))
if (!uri.Scheme.StartsWith("http", StringComparison.OrdinalIgnoreCase))
throw new ArgumentException($"The input {uri} is not a valid url", nameof(uri));
try
{
finalHandler ??= new HttpClientHandler()
{
SslProtocols = System.Security.Authentication.SslProtocols.Tls12,
};
using var httpClient = new HttpClient(finalHandler)
{
DefaultRequestVersion = HttpVersion.Version20
};
return await httpClient.GetStreamAsync(uri, cancellationToken);
return await s_httpClient.Value.GetStreamAsync(uri, cancellationToken).ConfigureAwait(false);
}
catch (HttpRequestException ex)
{
Expand Down
9 changes: 8 additions & 1 deletion src/lib/TypeExtensions/ApiManifestDocumentExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static async Task<OpenAIPluginManifest> ToOpenAIPluginManifestAsync(this
}
else
{
var result = await ParsingHelpers.ParseOpenApiAsync(new Uri(apiDependency.ApiDescriptionUrl), false, cancellationToken);
var result = await ParsingHelpers.ParseOpenApiAsync(new Uri(apiDependency.ApiDescriptionUrl), false, cancellationToken).ConfigureAwait(false);
return apiManifestDocument.ToOpenAIPluginManifest(result.OpenApiDocument, logoUrl, legalInfoUrl, openApiPath ?? apiDependency.ApiDescriptionUrl);
}
}
Expand Down Expand Up @@ -59,6 +59,13 @@ public static OpenAIPluginManifest ToOpenAIPluginManifest(this ApiManifestDocume
return openApiManifest;
}

/// <summary>
/// Tries to get an <see cref="ApiDependency"/> from the provided <see cref="ApiDependencies"/>.
/// </summary>
/// <param name="apiDependencies">The <see cref="ApiDependencies"/> to search for the apiDependency.</param>
/// <param name="apiDependencyName">The name of apiDependency to use from the provided <see cref="ApiManifestDocument.ApiDependencies"/>. The method defaults to the first apiDependency in <see cref="ApiManifestDocument.ApiDependencies"/> if no value is provided.</param>
/// <param name="apiDependency">The <see cref="ApiDependency"/> that was found.</param>
/// <returns>Returns true if the apiDependency is found and not null, otherwise false.</returns>
private static bool TryGetApiDependency(ApiDependencies apiDependencies, string? apiDependencyName, out ApiDependency? apiDependency)
{
if (apiDependencyName == default)
Expand Down
1 change: 0 additions & 1 deletion tests/ApiManifest.Tests/ApiManifest.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="Moq" Version="4.20.69" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
27 changes: 2 additions & 25 deletions tests/ApiManifest.Tests/Helpers/ParsingHelpersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
// Licensed under the MIT license.

using Microsoft.OpenApi.ApiManifest.Helpers;
using Moq;
using Moq.Protected;
using System.Net;
using System.Net.Http.Headers;
using System.Text.Json;

namespace Microsoft.OpenApi.ApiManifest.Tests.Helpers
Expand Down Expand Up @@ -121,11 +117,8 @@ public void ParseKeyValuePair()
public async Task ParseOpenApiAsync()
{
var testOpenApiFilePath = Path.Combine(".", "TestFiles", "testOpenApi.yaml");
var mockHandler = MockHttpResponse(File.ReadAllText(testOpenApiFilePath));

var openApiUri = new Uri("https://contoso.com/openapi.yaml");
var stream = await ParsingHelpers.GetStreamAsync(openApiUri, mockHandler, CancellationToken.None);
var results = await ParsingHelpers.ParseOpenApiAsync(stream, openApiUri, false, CancellationToken.None);
using var stream = File.OpenRead(testOpenApiFilePath);
var results = await ParsingHelpers.ParseOpenApiAsync(stream, new Uri("https://contoso.com/openapi.yaml"), false, CancellationToken.None);
Assert.Empty(results.OpenApiDiagnostic.Errors);
Assert.NotNull(results.OpenApiDocument);
}
Expand All @@ -143,21 +136,5 @@ public void ParseOpenApiWithOpenApiUrlWithAnInvalidSchema()
var openApiUri = new Uri("xyx://contoso.com/openapi.yaml");
_ = Assert.ThrowsAsync<ArgumentException>(async () => await ParsingHelpers.ParseOpenApiAsync(openApiUri, false, CancellationToken.None));
}

private static DelegatingHandler MockHttpResponse(string responseContent)
{
var mockResponse = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(responseContent) };
mockResponse.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

var mockHandler = new Mock<DelegatingHandler>();
_ = mockHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.Returns(Task.FromResult(mockResponse));
return mockHandler.Object;
}
}
}

0 comments on commit 4b71724

Please sign in to comment.