Skip to content

Commit d1a7eae

Browse files
committed
add performance enhancements
1 parent 408920b commit d1a7eae

File tree

7 files changed

+75
-36
lines changed

7 files changed

+75
-36
lines changed

.github/copilot-instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- @azure Rule - Use Azure Best Practices: When generating code for Azure, running terminal commands for Azure, or performing operations related to Azure, invoke your `get_azure_best_practices` tool if available.

Directory.Build.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
</PropertyGroup>
2626

2727
<PropertyGroup Label="Project Defaults">
28-
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
28+
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
2929
</PropertyGroup>
3030

3131
<PropertyGroup Label="C#">
32-
<LangVersion>12.0</LangVersion>
32+
<LangVersion>preview</LangVersion>
3333
<Nullable>enable</Nullable>
3434
<ImplicitUsings>enable</ImplicitUsings>
3535
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

global.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"sdk": {
3-
"version": "9.0.100"
4-
}
2+
"sdk": {
3+
"version": "10.0.100-preview.2.25164.34"
4+
}
55
}
Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright © myCSharp.de - all rights reserved
22

33
using Microsoft.AspNetCore.Http;
4+
using Microsoft.Extensions.Primitives;
45

56
namespace MyCSharp.HttpClientHints.AspNetCore;
67

@@ -9,15 +10,28 @@ namespace MyCSharp.HttpClientHints.AspNetCore;
910
/// </summary>
1011
public static class HttpClientHintsHttpContextExtensions
1112
{
13+
/// <summary>
14+
/// The cache key used to store the client hints in the HttpContext.Items dictionary.
15+
/// </summary>
16+
private const string ClientHintsCacheKey = "__HttpClientHints";
17+
1218
/// <summary>
1319
/// Retrieves the <see cref="HttpClientHints"/> from the current HTTP context.
1420
/// </summary>
1521
/// <param name="context">The HTTP context containing the request headers.</param>
1622
/// <returns>An instance of <see cref="HttpClientHints"/> populated with the relevant header values.</returns>
1723
public static HttpClientHints GetClientHints(this HttpContext context)
1824
{
19-
IHeaderDictionary headers = context.Request.Headers;
20-
return headers.GetClientHints();
25+
// Check if client hints are already cached for this request
26+
if (context.Items.TryGetValue(ClientHintsCacheKey, out var cached) && cached is HttpClientHints hints)
27+
{
28+
return hints;
29+
}
30+
31+
// Create and cache new client hints
32+
var newHints = context.Request.Headers.GetClientHints();
33+
context.Items[ClientHintsCacheKey] = newHints;
34+
return newHints;
2135
}
2236

2337
/// <summary>
@@ -27,25 +41,36 @@ public static HttpClientHints GetClientHints(this HttpContext context)
2741
/// <returns>An instance of <see cref="HttpClientHints"/> populated with the relevant header values.</returns>
2842
public static HttpClientHints GetClientHints(this IHeaderDictionary headers)
2943
{
30-
// user agent
31-
string? userAgent = headers["User-Agent"].FirstOrDefault();
32-
string? ua = headers["Sec-CH-UA"].FirstOrDefault();
44+
// User Agent
45+
headers.TryGetValue("User-Agent", out StringValues userAgentValues);
46+
string? userAgent = userAgentValues.Count > 0 ? userAgentValues[0] : null;
47+
48+
headers.TryGetValue("Sec-CH-UA", out StringValues uaValues);
49+
string? ua = uaValues.Count > 0 ? uaValues[0] : null;
3350

34-
// platform
35-
string? platform = headers["Sec-CH-UA-Platform"].FirstOrDefault();
36-
string? platformVersion = headers["Sec-CH-UA-Platform-Version"].FirstOrDefault();
51+
// Platform
52+
headers.TryGetValue("Sec-CH-UA-Platform", out StringValues platformValues);
53+
string? platform = platformValues.Count > 0 ? platformValues[0] : null;
54+
55+
headers.TryGetValue("Sec-CH-UA-Platform-Version", out StringValues platformVersionValues);
56+
string? platformVersion = platformVersionValues.Count > 0 ? platformVersionValues[0] : null;
3757

38-
// architecture
39-
string? architecture = headers["Sec-CH-UA-Arch"].FirstOrDefault();
58+
// Architecture
59+
headers.TryGetValue("Sec-CH-UA-Arch", out StringValues architectureValues);
60+
string? architecture = architectureValues.Count > 0 ? architectureValues[0] : null;
4061

41-
// other
42-
string? fullVersionList = headers["Sec-CH-UA-Full-Version-List"].FirstOrDefault();
62+
// Other
63+
headers.TryGetValue("Sec-CH-UA-Full-Version-List", out StringValues fullVersionListValues);
64+
string? fullVersionList = fullVersionListValues.Count > 0 ? fullVersionListValues[0] : null;
4365

44-
// device
45-
string? model = headers["Sec-CH-UA-Model"].FirstOrDefault();
46-
bool? mobile = HttpClientHintsInterpreter.IsMobile(headers["Sec-CH-UA-Mobile"].FirstOrDefault());
66+
// Device
67+
headers.TryGetValue("Sec-CH-UA-Model", out StringValues modelValues);
68+
string? model = modelValues.Count > 0 ? modelValues[0] : null;
69+
70+
headers.TryGetValue("Sec-CH-UA-Mobile", out StringValues mobileValues);
71+
bool? mobile = HttpClientHintsInterpreter.IsMobile(mobileValues.Count > 0 ? mobileValues[0] : null);
4772

48-
// return the HttpClientHints record
73+
// Return the HttpClientHints record
4974
return new(userAgent, platform, platformVersion, architecture, model, fullVersionList, ua, mobile, headers);
5075
}
5176
}

src/MyCSharp.HttpClientHints.AspNetCore/HttpClientHintsMiddlewareConfig.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,32 @@ public class HttpClientHintsMiddlewareConfig
1212
/// </summary>
1313
/// <value>The name of the response header as a <see cref="string"/>.</value>
1414
/// <remarks>These settings are set by <see cref="HttpClientHintsRegistration"/>. Do not set these values manually.</remarks>
15-
public required string ResponseHeader { get; set; }
15+
public required string ResponseHeader
16+
{
17+
get;
18+
set
19+
{
20+
field = value;
21+
HasResponseHeaders = string.IsNullOrEmpty(value) is false;
22+
}
23+
}
24+
25+
public bool HasResponseHeaders { get; internal set; }
1626

1727
/// <summary>
1828
/// Gets or sets the lifetime of the client hints in seconds.
1929
/// </summary>
2030
/// <value>A <see cref="string"/> containing an <see cref="int"/> representing the lifetime in seconds, or <c>null</c> if unspecified.</value>
2131
/// <remarks>These settings are set by <see cref="HttpClientHintsRegistration"/>. Do not set these values manually.</remarks>
22-
public string? LifeTime { get; set; }
32+
public string? LifeTime
33+
{
34+
get;
35+
set
36+
{
37+
field = value;
38+
HasLifetime = string.IsNullOrEmpty(value) is false;
39+
}
40+
}
41+
42+
public bool HasLifetime { get; internal set; }
2343
}

src/MyCSharp.HttpClientHints.AspNetCore/HttpClientHintsRequestMiddleware.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
1-
// Copyright © myCSharp.de - all rights reserved
2-
31
using Microsoft.AspNetCore.Http;
42
using Microsoft.Extensions.Options;
53

64
namespace MyCSharp.HttpClientHints.AspNetCore;
75

8-
/// <summary>
9-
/// Middleware for adding HTTP Client Hints headers to the response.
10-
/// Initializes a new instance of the <see cref="HttpClientHintsRequestMiddleware"/> class.
11-
/// </summary>
12-
/// <param name="next">The next middleware in the request pipeline.</param>
13-
/// <param name="options">The options for configuring the middleware.</param>
146
public class HttpClientHintsRequestMiddleware(RequestDelegate next, IOptions<HttpClientHintsMiddlewareConfig> options)
157
{
8+
// Cache the options value and pre-compute conditions to avoid repeated checks
169
private readonly HttpClientHintsMiddlewareConfig _options = options.Value;
1710

1811
/// <summary>
@@ -22,12 +15,12 @@ public class HttpClientHintsRequestMiddleware(RequestDelegate next, IOptions<Htt
2215
/// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
2316
public async Task InvokeAsync(HttpContext context)
2417
{
25-
// Add Client Hints headers to the response
26-
if (string.IsNullOrEmpty(_options.ResponseHeader) is false)
18+
if (_options.HasResponseHeaders)
2719
{
20+
// Set headers directly without additional checks
2821
context.Response.Headers["Accept-CH"] = _options.ResponseHeader;
2922

30-
if (_options.LifeTime is not null)
23+
if (_options.HasLifetime)
3124
{
3225
context.Response.Headers["Accept-CH-Lifetime"] = _options.LifeTime;
3326
}

src/MyCSharp.HttpClientHints/HttpClientHintsInterpreter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ public static class HttpClientHintsInterpreter
2121
/// </returns>
2222
public static bool? IsMobile(string? mobileHeaderValue)
2323
{
24-
if (mobileHeaderValue is "?1")
24+
if (string.Equals(mobileHeaderValue, "?1", StringComparison.Ordinal))
2525
{
2626
return true;
2727
}
2828

29-
if (mobileHeaderValue is "?0")
29+
if (string.Equals(mobileHeaderValue, "?0", StringComparison.Ordinal))
3030
{
3131
return false;
3232
}

0 commit comments

Comments
 (0)