Skip to content

add performance enhancements #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +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.
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@
</PropertyGroup>

<PropertyGroup Label="Project Defaults">
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup Label="C#">
<LangVersion>12.0</LangVersion>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
Expand Down
6 changes: 3 additions & 3 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"sdk": {
"version": "9.0.100"
}
"sdk": {
"version": "10.0.100-preview.2.25164.34"
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright © myCSharp.de - all rights reserved

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;

namespace MyCSharp.HttpClientHints.AspNetCore;

Expand All @@ -9,15 +10,28 @@ namespace MyCSharp.HttpClientHints.AspNetCore;
/// </summary>
public static class HttpClientHintsHttpContextExtensions
{
/// <summary>
/// The cache key used to store the client hints in the HttpContext.Items dictionary.
/// </summary>
private const string ClientHintsCacheKey = "__HttpClientHints";

/// <summary>
/// Retrieves the <see cref="HttpClientHints"/> from the current HTTP context.
/// </summary>
/// <param name="context">The HTTP context containing the request headers.</param>
/// <returns>An instance of <see cref="HttpClientHints"/> populated with the relevant header values.</returns>
public static HttpClientHints GetClientHints(this HttpContext context)
{
IHeaderDictionary headers = context.Request.Headers;
return headers.GetClientHints();
// Check if client hints are already cached for this request
if (context.Items.TryGetValue(ClientHintsCacheKey, out var cached) && cached is HttpClientHints hints)
{
return hints;
}

// Create and cache new client hints
var newHints = context.Request.Headers.GetClientHints();
context.Items[ClientHintsCacheKey] = newHints;
return newHints;
}

/// <summary>
Expand All @@ -27,25 +41,36 @@ public static HttpClientHints GetClientHints(this HttpContext context)
/// <returns>An instance of <see cref="HttpClientHints"/> populated with the relevant header values.</returns>
public static HttpClientHints GetClientHints(this IHeaderDictionary headers)
{
// user agent
string? userAgent = headers["User-Agent"].FirstOrDefault();
string? ua = headers["Sec-CH-UA"].FirstOrDefault();
// User Agent
headers.TryGetValue("User-Agent", out StringValues userAgentValues);
string? userAgent = userAgentValues.Count > 0 ? userAgentValues[0] : null;

headers.TryGetValue("Sec-CH-UA", out StringValues uaValues);
string? ua = uaValues.Count > 0 ? uaValues[0] : null;

// platform
string? platform = headers["Sec-CH-UA-Platform"].FirstOrDefault();
string? platformVersion = headers["Sec-CH-UA-Platform-Version"].FirstOrDefault();
// Platform
headers.TryGetValue("Sec-CH-UA-Platform", out StringValues platformValues);
string? platform = platformValues.Count > 0 ? platformValues[0] : null;

headers.TryGetValue("Sec-CH-UA-Platform-Version", out StringValues platformVersionValues);
string? platformVersion = platformVersionValues.Count > 0 ? platformVersionValues[0] : null;

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

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

// device
string? model = headers["Sec-CH-UA-Model"].FirstOrDefault();
bool? mobile = HttpClientHintsInterpreter.IsMobile(headers["Sec-CH-UA-Mobile"].FirstOrDefault());
// Device
headers.TryGetValue("Sec-CH-UA-Model", out StringValues modelValues);
string? model = modelValues.Count > 0 ? modelValues[0] : null;

headers.TryGetValue("Sec-CH-UA-Mobile", out StringValues mobileValues);
bool? mobile = HttpClientHintsInterpreter.IsMobile(mobileValues.Count > 0 ? mobileValues[0] : null);

// return the HttpClientHints record
// Return the HttpClientHints record
return new(userAgent, platform, platformVersion, architecture, model, fullVersionList, ua, mobile, headers);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,32 @@
/// </summary>
/// <value>The name of the response header as a <see cref="string"/>.</value>
/// <remarks>These settings are set by <see cref="HttpClientHintsRegistration"/>. Do not set these values manually.</remarks>
public required string ResponseHeader { get; set; }
public required string ResponseHeader
{
get;
set
{
field = value;
HasResponseHeaders = string.IsNullOrEmpty(value) is false;
}
}

public bool HasResponseHeaders { get; internal set; }

Check failure on line 25 in src/MyCSharp.HttpClientHints.AspNetCore/HttpClientHintsMiddlewareConfig.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'HttpClientHintsMiddlewareConfig.HasResponseHeaders'

Check failure on line 25 in src/MyCSharp.HttpClientHints.AspNetCore/HttpClientHintsMiddlewareConfig.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'HttpClientHintsMiddlewareConfig.HasResponseHeaders'

Check failure on line 25 in src/MyCSharp.HttpClientHints.AspNetCore/HttpClientHintsMiddlewareConfig.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'HttpClientHintsMiddlewareConfig.HasResponseHeaders'

/// <summary>
/// Gets or sets the lifetime of the client hints in seconds.
/// </summary>
/// <value>A <see cref="string"/> containing an <see cref="int"/> representing the lifetime in seconds, or <c>null</c> if unspecified.</value>
/// <remarks>These settings are set by <see cref="HttpClientHintsRegistration"/>. Do not set these values manually.</remarks>
public string? LifeTime { get; set; }
public string? LifeTime
{
get;
set
{
field = value;
HasLifetime = string.IsNullOrEmpty(value) is false;
}
}

public bool HasLifetime { get; internal set; }

Check failure on line 42 in src/MyCSharp.HttpClientHints.AspNetCore/HttpClientHintsMiddlewareConfig.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'HttpClientHintsMiddlewareConfig.HasLifetime'

Check failure on line 42 in src/MyCSharp.HttpClientHints.AspNetCore/HttpClientHintsMiddlewareConfig.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'HttpClientHintsMiddlewareConfig.HasLifetime'

Check failure on line 42 in src/MyCSharp.HttpClientHints.AspNetCore/HttpClientHintsMiddlewareConfig.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'HttpClientHintsMiddlewareConfig.HasLifetime'
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
// Copyright © myCSharp.de - all rights reserved

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;

namespace MyCSharp.HttpClientHints.AspNetCore;

/// <summary>
/// Middleware for adding HTTP Client Hints headers to the response.
/// Initializes a new instance of the <see cref="HttpClientHintsRequestMiddleware"/> class.
/// </summary>
/// <param name="next">The next middleware in the request pipeline.</param>
/// <param name="options">The options for configuring the middleware.</param>
public class HttpClientHintsRequestMiddleware(RequestDelegate next, IOptions<HttpClientHintsMiddlewareConfig> options)

Check failure on line 6 in src/MyCSharp.HttpClientHints.AspNetCore/HttpClientHintsRequestMiddleware.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'HttpClientHintsRequestMiddleware.HttpClientHintsRequestMiddleware(RequestDelegate, IOptions<HttpClientHintsMiddlewareConfig>)'

Check failure on line 6 in src/MyCSharp.HttpClientHints.AspNetCore/HttpClientHintsRequestMiddleware.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'HttpClientHintsRequestMiddleware'

Check failure on line 6 in src/MyCSharp.HttpClientHints.AspNetCore/HttpClientHintsRequestMiddleware.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'HttpClientHintsRequestMiddleware.HttpClientHintsRequestMiddleware(RequestDelegate, IOptions<HttpClientHintsMiddlewareConfig>)'

Check failure on line 6 in src/MyCSharp.HttpClientHints.AspNetCore/HttpClientHintsRequestMiddleware.cs

View workflow job for this annotation

GitHub Actions / build / build

Missing XML comment for publicly visible type or member 'HttpClientHintsRequestMiddleware'
{
// Cache the options value and pre-compute conditions to avoid repeated checks
private readonly HttpClientHintsMiddlewareConfig _options = options.Value;

/// <summary>
Expand All @@ -22,12 +15,12 @@
/// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
public async Task InvokeAsync(HttpContext context)
{
// Add Client Hints headers to the response
if (string.IsNullOrEmpty(_options.ResponseHeader) is false)
if (_options.HasResponseHeaders)
{
// Set headers directly without additional checks
context.Response.Headers["Accept-CH"] = _options.ResponseHeader;

if (_options.LifeTime is not null)
if (_options.HasLifetime)
{
context.Response.Headers["Accept-CH-Lifetime"] = _options.LifeTime;
}
Expand Down
4 changes: 2 additions & 2 deletions src/MyCSharp.HttpClientHints/HttpClientHintsInterpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ public static class HttpClientHintsInterpreter
/// </returns>
public static bool? IsMobile(string? mobileHeaderValue)
{
if (mobileHeaderValue is "?1")
if (string.Equals(mobileHeaderValue, "?1", StringComparison.Ordinal))
{
return true;
}

if (mobileHeaderValue is "?0")
if (string.Equals(mobileHeaderValue, "?0", StringComparison.Ordinal))
{
return false;
}
Expand Down