Skip to content

Conversation

@alex-clickhouse
Copy link
Collaborator

No description provided.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enhances the HTTP User-Agent header to include operating system, runtime, and architecture information alongside the driver version. The implementation introduces a new UserAgentProvider utility class that lazily caches system information to avoid repeated lookups on every HTTP request, improving performance while providing richer diagnostic information to the ClickHouse server.

Key Changes:

  • Created a new UserAgentProvider utility class with lazy-initialized, cached system information
  • Refactored ClickHouseConnection.AddDefaultHttpHeaders() to use the cached user agent provider
  • User-Agent header now includes platform, OS description, runtime version, and process architecture

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
ClickHouse.Driver/Utility/UserAgentProvider.cs New utility class providing cached user agent information including driver version and system details
ClickHouse.Driver/ADO/ClickHouseConnection.cs Updated to use UserAgentProvider for HTTP headers, replacing inline version lookup


// Pre-build ProductInfoHeaderValue objects
DriverProductInfo = new ProductInfoHeaderValue("ClickHouse.Driver", version);
SystemProductInfo = new ProductInfoHeaderValue($"(platform:{osPlatform}; os:{osDescription}; runtime:{runtime}; arch:{architecture})");
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The osDescription field may contain sensitive or unpredictable characters that could break HTTP header formatting or expose unnecessary system details. User-Agent strings should be sanitized to remove special characters (newlines, control characters) and potentially truncated to prevent excessively long headers. Consider sanitizing osDescription before including it in the header.

Copilot uses AI. Check for mistakes.
/// </summary>
internal static class UserAgentProvider
{
private static readonly Lazy<CachedUserAgentInfo> LazyInfo = new Lazy<CachedUserAgentInfo>(() => new CachedUserAgentInfo());
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The Lazy<T> constructor without isThreadSafe parameter defaults to thread-safe mode (LazyThreadSafetyMode.ExecutionAndPublication), which is correct for static fields. However, for clarity and to make thread-safety explicit in this multi-threaded database client, consider using new Lazy<CachedUserAgentInfo>(() => new CachedUserAgentInfo(), isThreadSafe: true) or the LazyThreadSafetyMode overload.

Copilot uses AI. Check for mistakes.
Comment on lines 27 to 28
var version = versionAndHash.Split('+')[0];

Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If InformationalVersion returns null (line 26), it falls back to "unknown", but if it returns an empty string or a string without a '+' separator, Split('+')[0] will return the entire string. However, if Split is called on "unknown" (which has no '+'), this works correctly. The issue is that this assumes the version string always follows the pattern 'version+hash', but SemVer strings may not contain '+'. Consider using version = versionAndHash.Contains('+') ? versionAndHash.Split('+')[0] : versionAndHash; for robustness.

Suggested change
var version = versionAndHash.Split('+')[0];
var version = versionAndHash.Contains('+') ? versionAndHash.Split('+')[0] : versionAndHash;
if (string.IsNullOrWhiteSpace(version))
version = "unknown";

Copilot uses AI. Check for mistakes.
var architecture = System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture.ToString();

// Get runtime information
var runtime = Environment.Version.ToString();
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Environment.Version returns the CLR version, not the runtime version (e.g., .NET 6.0, .NET Framework 4.8). For .NET Core/.NET 5+, RuntimeInformation.FrameworkDescription provides the actual runtime version string (e.g., '.NET 6.0.1'). Consider using System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription instead to provide more accurate runtime information consistent with other runtime metadata.

Suggested change
var runtime = Environment.Version.ToString();
var runtime = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;

Copilot uses AI. Check for mistakes.
@codecov
Copy link

codecov bot commented Nov 17, 2025

Codecov Report

❌ Patch coverage is 72.72727% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
ClickHouse.Driver/Utility/UserAgentProvider.cs 72.72% 4 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@alex-clickhouse alex-clickhouse merged commit 8a97b6f into main Nov 18, 2025
16 of 17 checks passed
@alex-clickhouse alex-clickhouse deleted the user-agent branch November 18, 2025 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants