Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
67 changes: 67 additions & 0 deletions DnsClientX.Tests/ResolveFromRootTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;

Expand All @@ -17,5 +19,70 @@
Assert.Equal(DnsRecordType.A, ans.Type);
}
}

[Fact]
/// <summary>

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / macOS

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / macOS

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Ubuntu

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Ubuntu

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Ubuntu PowerShell 7

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Ubuntu PowerShell 7

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / macOS PowerShell 7

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / macOS PowerShell 7

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 7

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 7

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 7

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 7

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 5.1

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 5.1

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 5.1

XML comment is not placed on a valid language element

Check failure on line 24 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 5.1

XML comment is not placed on a valid language element
/// Ensures root lookups created in parallel keep their clients alive until completion.
/// </summary>
Comment on lines +23 to +26
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The XML documentation comment should be placed before the [Fact] attribute, not after it. The standard convention is to place documentation comments immediately before attributes.

Suggested change
[Fact]
/// <summary>
/// Ensures root lookups created in parallel keep their clients alive until completion.
/// </summary>
/// <summary>
/// Ensures root lookups created in parallel keep their clients alive until completion.
/// </summary>
[Fact]

Copilot uses AI. Check for mistakes.
public async Task QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly() {

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / macOS

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / macOS

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Ubuntu

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Ubuntu

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Ubuntu PowerShell 7

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Ubuntu PowerShell 7

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / macOS PowerShell 7

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / macOS PowerShell 7

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 7

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 7

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 7

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 7

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 5.1

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 5.1

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 5.1

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'

Check failure on line 27 in DnsClientX.Tests/ResolveFromRootTests.cs

View workflow job for this annotation

GitHub Actions / Windows PowerShell 5.1

Missing XML comment for publicly visible type or member 'ResolveFromRootTests.QueryDns_MultipleRootLookups_DoesNotDisposeClientsEarly()'
var createdClients = new List<TrackingClientX>();
var completions = new List<TaskCompletionSource<DnsResponse>>();
Func<ClientX> originalFactory = ClientX.RootClientFactory;
var originalResolver = ClientX.RootResolveOverride;

try {
ClientX.RootClientFactory = () => {
var client = new TrackingClientX();
createdClients.Add(client);
return client;
};

ClientX.RootResolveOverride = (client, name, recordType, cancellationToken) => {
var tcs = new TaskCompletionSource<DnsResponse>(TaskCreationOptions.RunContinuationsAsynchronously);
completions.Add(tcs);
return tcs.Task;
};

Task<DnsResponse[]> queryTask = ClientX.QueryDns(new[] { "example.com", "example.net" }, DnsRecordType.A, DnsEndpoint.RootServer);

await Task.Delay(10);
Assert.Equal(2, completions.Count);
Assert.All(createdClients, client => Assert.False(client.IsDisposed));

foreach (var completion in completions) {
completion.SetResult(new DnsResponse {
Answers = new[] {
new DnsAnswer {
Name = "example.com",
Type = DnsRecordType.A,
TTL = 60,
DataRaw = "127.0.0.1"
}
}
});
}

var responses = await queryTask;
Assert.Equal(2, responses.Length);
Assert.All(createdClients, client => Assert.True(client.IsDisposed));
} finally {
ClientX.RootClientFactory = originalFactory;
ClientX.RootResolveOverride = originalResolver;
}
}

private sealed class TrackingClientX : ClientX {
public bool IsDisposed { get; private set; }

protected override void Dispose(bool disposing) {
base.Dispose(disposing);
IsDisposed = true;
}

protected override async ValueTask DisposeAsyncCore() {
await base.DisposeAsyncCore().ConfigureAwait(false);
IsDisposed = true;
}
}
}
}
1 change: 1 addition & 0 deletions DnsClientX/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("DnsClientX.Tests")]
31 changes: 24 additions & 7 deletions DnsClientX/DnsClientX.QueryDns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ namespace DnsClientX {
/// Provides synchronous and asynchronous methods for performing DNS lookups.
/// </remarks>
public partial class ClientX {
internal static Func<ClientX> RootClientFactory { get; set; } = () => new ClientX();

internal static Func<ClientX, string, DnsRecordType, CancellationToken, Task<DnsResponse>>? RootResolveOverride { get; set; }

/// <summary>
/// Sends a DNS query for a specific record type to a DNS server.
/// This method allows you to specify the DNS endpoint from a predefined list of endpoints.
Expand Down Expand Up @@ -91,14 +95,27 @@ public static DnsResponse QueryDnsSync(string name, DnsRecordType recordType, Dn
/// <returns>A task that represents the asynchronous operation. The task result contains the DNS response.</returns>
public static async Task<DnsResponse[]> QueryDns(string[] name, DnsRecordType recordType, DnsEndpoint dnsEndpoint = DnsEndpoint.System, DnsSelectionStrategy dnsSelectionStrategy = DnsSelectionStrategy.First, int timeOutMilliseconds = Configuration.DefaultTimeout, bool retryOnTransient = true, int maxRetries = 3, int retryDelayMs = 200, bool requestDnsSec = false, bool validateDnsSec = false, bool typedRecords = false, bool parseTypedTxtRecords = false, CancellationToken cancellationToken = default) {
Comment on lines 95 to 96
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

[nitpick] The method doesn't validate if the name array is null or empty. When an empty array is passed, the method will create an empty clients list and return an empty array of DnsResponse, which may be the intended behavior. However, consider adding explicit null/empty validation or documenting this behavior.

Suggested change
/// <returns>A task that represents the asynchronous operation. The task result contains the DNS response.</returns>
public static async Task<DnsResponse[]> QueryDns(string[] name, DnsRecordType recordType, DnsEndpoint dnsEndpoint = DnsEndpoint.System, DnsSelectionStrategy dnsSelectionStrategy = DnsSelectionStrategy.First, int timeOutMilliseconds = Configuration.DefaultTimeout, bool retryOnTransient = true, int maxRetries = 3, int retryDelayMs = 200, bool requestDnsSec = false, bool validateDnsSec = false, bool typedRecords = false, bool parseTypedTxtRecords = false, CancellationToken cancellationToken = default) {
/// <returns>A task that represents the asynchronous operation. The task result contains the DNS response.</returns>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="name"/> is null.</exception>
/// <remarks>
/// If <paramref name="name"/> is an empty array, the method returns an empty <see cref="DnsResponse"/> array.
/// </remarks>
public static async Task<DnsResponse[]> QueryDns(string[] name, DnsRecordType recordType, DnsEndpoint dnsEndpoint = DnsEndpoint.System, DnsSelectionStrategy dnsSelectionStrategy = DnsSelectionStrategy.First, int timeOutMilliseconds = Configuration.DefaultTimeout, bool retryOnTransient = true, int maxRetries = 3, int retryDelayMs = 200, bool requestDnsSec = false, bool validateDnsSec = false, bool typedRecords = false, bool parseTypedTxtRecords = false, CancellationToken cancellationToken = default) {
if (name == null) {
throw new ArgumentNullException(nameof(name));
}
if (name.Length == 0) {
return Array.Empty<DnsResponse>();
}

Copilot uses AI. Check for mistakes.
if (dnsEndpoint == DnsEndpoint.RootServer) {
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

[nitpick] Consider checking cancellationToken.IsCancellationRequested before the LINQ query (line 100) to avoid creating and disposing clients unnecessarily when the operation is already cancelled.

Suggested change
if (dnsEndpoint == DnsEndpoint.RootServer) {
if (dnsEndpoint == DnsEndpoint.RootServer) {
if (cancellationToken.IsCancellationRequested) {
return await Task.FromCanceled<DnsResponse[]>(cancellationToken).ConfigureAwait(false);
}

Copilot uses AI. Check for mistakes.
var tasks = name.Select(n => {
using var client = new ClientX();
if (cancellationToken.IsCancellationRequested) {
return Task.FromCanceled<DnsResponse>(cancellationToken);
var clients = new List<ClientX>(name.Length);
try {
var tasks = name.Select(n => {
var client = RootClientFactory();
clients.Add(client);
if (cancellationToken.IsCancellationRequested) {
return Task.FromCanceled<DnsResponse>(cancellationToken);
}

var resolver = RootResolveOverride;
return resolver != null
? resolver(client, n, recordType, cancellationToken)
: client.ResolveFromRoot(n, recordType, cancellationToken: cancellationToken);
}).ToArray();

Comment on lines +99 to +112
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

This variable is manually disposed in a finally block - consider a C# using statement as a preferable resource management technique.

Suggested change
try {
var tasks = name.Select(n => {
var client = RootClientFactory();
clients.Add(client);
if (cancellationToken.IsCancellationRequested) {
return Task.FromCanceled<DnsResponse>(cancellationToken);
}
var resolver = RootResolveOverride;
return resolver != null
? resolver(client, n, recordType, cancellationToken)
: client.ResolveFromRoot(n, recordType, cancellationToken: cancellationToken);
}).ToArray();
var tasks = name.Select(n => {
var client = RootClientFactory();
clients.Add(client);
if (cancellationToken.IsCancellationRequested) {
return Task.FromCanceled<DnsResponse>(cancellationToken);
}
var resolver = RootResolveOverride;
return resolver != null
? resolver(client, n, recordType, cancellationToken)
: client.ResolveFromRoot(n, recordType, cancellationToken: cancellationToken);
}).ToArray();
try {

Copilot uses AI. Check for mistakes.
return await Task.WhenAll(tasks).ConfigureAwait(false);
} finally {
foreach (var client in clients) {
client.Dispose();
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

If any Dispose() call throws an exception in the finally block, it will suppress exceptions from Task.WhenAll(). Consider wrapping the disposal loop in a try-catch to prevent disposal exceptions from masking task failures:

} finally {
    foreach (var client in clients) {
        try {
            client.Dispose();
        } catch {
            // Log or ignore disposal failures
        }
    }
}
Suggested change
client.Dispose();
try {
client.Dispose();
} catch {
// Log or ignore disposal failures
}

Copilot uses AI. Check for mistakes.
}
return client.ResolveFromRoot(n, recordType, cancellationToken: cancellationToken);
});
return await Task.WhenAll(tasks).ConfigureAwait(false);
}
} else {
using var client = new ClientX(endpoint: dnsEndpoint, dnsSelectionStrategy);
client.EndpointConfiguration.TimeOut = timeOutMilliseconds;
Expand Down
Loading