Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a144ef6
Add Json Payload Functionality for User Agent Feature Extension
samsharma2700 Aug 22, 2025
13090ce
Update truncation null checks
samsharma2700 Aug 22, 2025
8359565
Merge remote-tracking branch 'origin/main' into dev/samsharma2700/use…
samsharma2700 Sep 9, 2025
c450c61
Enable UserAgent Feature Extension
samsharma2700 Sep 9, 2025
c8897d3
Add new functional tests for UserAgent FE
samsharma2700 Sep 10, 2025
c0eea2b
Update functional test to verify driver behaviour
samsharma2700 Sep 11, 2025
26dd6f9
Assertion update
samsharma2700 Sep 11, 2025
8ad3c69
Remove unused flags and conditionals
samsharma2700 Sep 11, 2025
2184035
Remove IsUserAgentSupportEnabled flag
samsharma2700 Sep 11, 2025
af1eeb1
Merge remote-tracking branch 'origin/main' into dev/samsharma2700/use…
samsharma2700 Sep 11, 2025
3b51844
Test cleanup and identifier update
samsharma2700 Sep 11, 2025
ff39552
Merge remote-tracking branch 'origin/main' into dev/samsharma2700/use…
samsharma2700 Sep 17, 2025
2394a2f
Merge remote-tracking branch 'origin/main' into dev/samsharma2700/use…
samsharma2700 Sep 23, 2025
d673104
Fix server side throw issue
samsharma2700 Oct 7, 2025
90c97c5
Resolve merge conflicts
samsharma2700 Oct 7, 2025
d494b66
Add useragent payload in parser
samsharma2700 Oct 8, 2025
4b59a62
Update Test
samsharma2700 Oct 10, 2025
bd038a2
Remove stray comment
samsharma2700 Oct 10, 2025
92452b8
Cleanup
samsharma2700 Oct 10, 2025
74027ae
Make Uagent flag session based
samsharma2700 Oct 10, 2025
792a721
Merge remote-tracking branch 'origin/main' into dev/samsharma2700/use…
samsharma2700 Oct 10, 2025
4fc5055
Minor fix
samsharma2700 Oct 10, 2025
45b7132
Add LocalAppContextSwitch and CI fix
samsharma2700 Oct 13, 2025
d88b970
Merge remote-tracking branch 'origin/main' into dev/samsharma2700/use…
samsharma2700 Oct 13, 2025
f427f5d
Minor fix
samsharma2700 Oct 13, 2025
eb5410a
Remove stray debug
samsharma2700 Oct 21, 2025
feec76f
Merge remote-tracking branch 'origin/main' into dev/samsharma2700/use…
samsharma2700 Oct 21, 2025
e043d15
Merge remote-tracking branch 'origin/main' into dev/samsharma2700/use…
samsharma2700 Oct 22, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ internal bool IsDNSCachingBeforeRedirectSupported
internal bool IsJsonSupportEnabled = false;

// User Agent Flag
internal bool IsUserAgentEnabled = true;
internal bool IsUserAgentSupportEnabled = true;
Comment thread
paulmedynski marked this conversation as resolved.
Outdated

// Vector Support Flag
internal bool IsVectorSupportEnabled = false;
Expand Down Expand Up @@ -1417,7 +1417,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword,
requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching;
requestedFeatures |= TdsEnums.FeatureExtension.JsonSupport;
requestedFeatures |= TdsEnums.FeatureExtension.VectorSupport;

#if DEBUG
Comment thread
paulmedynski marked this conversation as resolved.
Outdated
requestedFeatures |= TdsEnums.FeatureExtension.UserAgent;
#endif
Expand Down Expand Up @@ -3000,6 +3000,12 @@ internal void OnFeatureExtAck(int featureId, byte[] data)
IsVectorSupportEnabled = true;
break;
}
case TdsEnums.FEATUREEXT_USERAGENT:
{
// Unexpected ack from server but we ignore it entirely
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.SqlInternalConnectionTds.OnFeatureExtAck|ADV> {0}, Received feature extension acknowledgement for USERAGENTSUPPORT (ignored)", ObjectID);
break;
}

default:
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
using Microsoft.Data.SqlClient.DataClassification;
using Microsoft.Data.SqlClient.LocalDb;
using Microsoft.Data.SqlClient.Server;
using Microsoft.Data.SqlClient.UserAgent;

#if NETFRAMEWORK
using Microsoft.Data.SqlTypes;
#endif
Expand Down Expand Up @@ -8868,7 +8870,15 @@ private void WriteLoginData(SqlLogin rec,
}
}

ApplyFeatureExData(requestedFeatures, recoverySessionData, fedAuthFeatureExtensionData, useFeatureExt, length, true);
ApplyFeatureExData(
requestedFeatures,
recoverySessionData,
fedAuthFeatureExtensionData,
UserAgentInfo.UserAgentCachedJsonPayload.ToArray(),
useFeatureExt,
length,
true
);
}
catch (Exception e)
{
Expand All @@ -8887,6 +8897,7 @@ private void WriteLoginData(SqlLogin rec,
private int ApplyFeatureExData(TdsEnums.FeatureExtension requestedFeatures,
SessionData recoverySessionData,
FederatedAuthenticationFeatureExtensionData fedAuthFeatureExtensionData,
byte[] userAgentJsonPayload,
bool useFeatureExt,
int length,
bool write = false)
Expand Down Expand Up @@ -8939,6 +8950,11 @@ private int ApplyFeatureExData(TdsEnums.FeatureExtension requestedFeatures,
length += WriteVectorSupportFeatureRequest(write);
}

if ((requestedFeatures & TdsEnums.FeatureExtension.UserAgent) != 0)
{
length += WriteUserAgentFeatureRequest(userAgentJsonPayload, write);
}

length++; // for terminator
if (write)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ internal bool IsDNSCachingBeforeRedirectSupported
internal bool IsVectorSupportEnabled = false;

// User Agent Flag
internal bool IsUserAgentEnabled = true;
internal bool IsUserAgentSupportEnabled = true;

// TCE flags
internal byte _tceVersionSupported;
Expand Down Expand Up @@ -3038,7 +3038,12 @@ internal void OnFeatureExtAck(int featureId, byte[] data)
IsVectorSupportEnabled = true;
break;
}

case TdsEnums.FEATUREEXT_USERAGENT:
{
// Unexpected ack from server but we ignore it entirely
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.SqlInternalConnectionTds.OnFeatureExtAck|ADV> {0}, Received feature extension acknowledgement for USERAGENTSUPPORT (ignored)", ObjectID);
break;
}
default:
{
// Unknown feature ack
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
using Microsoft.Data.SqlClient.DataClassification;
using Microsoft.Data.SqlClient.LocalDb;
using Microsoft.Data.SqlClient.Server;
using Microsoft.Data.SqlClient.UserAgent;

#if NETFRAMEWORK
using Microsoft.Data.SqlTypes;
#endif
Expand Down Expand Up @@ -9035,7 +9037,15 @@ private void WriteLoginData(SqlLogin rec,
}
}

ApplyFeatureExData(requestedFeatures, recoverySessionData, fedAuthFeatureExtensionData, useFeatureExt, length, true);
ApplyFeatureExData(
requestedFeatures,
recoverySessionData,
fedAuthFeatureExtensionData,
UserAgentInfo.UserAgentCachedJsonPayload.ToArray(),
useFeatureExt,
length,
true
);
}
catch (Exception e)
{
Expand All @@ -9054,6 +9064,7 @@ private void WriteLoginData(SqlLogin rec,
private int ApplyFeatureExData(TdsEnums.FeatureExtension requestedFeatures,
SessionData recoverySessionData,
FederatedAuthenticationFeatureExtensionData fedAuthFeatureExtensionData,
byte[] userAgentJsonPayload,
bool useFeatureExt,
int length,
bool write = false)
Expand Down Expand Up @@ -9108,6 +9119,11 @@ private int ApplyFeatureExData(TdsEnums.FeatureExtension requestedFeatures,
length += WriteVectorSupportFeatureRequest(write);
}

if ((requestedFeatures & TdsEnums.FeatureExtension.UserAgent) != 0)
{
length += WriteUserAgentFeatureRequest(userAgentJsonPayload, write);
}

length++; // for terminator
if (write)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Buffers;
using System.Diagnostics;
using System.Text;
using Microsoft.Data.SqlClient.UserAgent;
using Microsoft.Data.SqlClient.Utilities;

#nullable enable
Expand Down Expand Up @@ -192,7 +193,14 @@ internal void TdsLogin(

int feOffset = length;
// calculate and reserve the required bytes for the featureEx
length = ApplyFeatureExData(requestedFeatures, recoverySessionData, fedAuthFeatureExtensionData, useFeatureExt, length);
length = ApplyFeatureExData(
Comment thread
samsharma2700 marked this conversation as resolved.
Outdated
requestedFeatures,
recoverySessionData,
fedAuthFeatureExtensionData,
UserAgentInfo.UserAgentCachedJsonPayload.ToArray(),
useFeatureExt,
length
);

WriteLoginData(rec,
requestedFeatures,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -620,5 +620,116 @@ public void TestConnWithVectorFeatExtVersionNegotiation(bool expectedConnectionR
Assert.Throws<InvalidOperationException>(() => connection.Open());
}
}

// Test to verify client sends a UserAgent version
// We do not receive any Ack for it from the server
[Fact]
public void TestConnWithUnackedUserAgentFeatureExtension()
{
using var server = TestTdsServer.StartTestServer();

// Configure the server to support UserAgent version 0x01
server.ServerSupportedUserAgentFeatureExtVersion = 0x01;
server.EnableUserAgentFeatureExt = true;
// By design its response logic never emits an ACK
bool loginFound = false;
bool responseFound = false;

// Inspect what the client sends in the LOGIN7 packet
server.OnLogin7Validated = loginToken =>
{
var token = loginToken.FeatureExt
.OfType<TDSLogin7GenericOptionToken>()
.FirstOrDefault(t => t.FeatureID == TDSFeatureID.UserAgentSupport);
if (token != null)
Comment thread
paulmedynski marked this conversation as resolved.
Outdated
{
Assert.Equal((byte)TDSFeatureID.UserAgentSupport, (byte)token.FeatureID);
Assert.Equal(0x1, token.Data[0]);
loginFound = true;
Comment thread
paulmedynski marked this conversation as resolved.
Outdated
}
};

// Inspect whether the server ever sends back an ACK
server.OnAuthenticationResponseCompleted = response =>
Comment thread
paulmedynski marked this conversation as resolved.
Outdated
{
var ack = response
.OfType<TDSFeatureExtAckToken>()
.SelectMany(t => t.Options)
.OfType<TDSFeatureExtAckGenericOption>()
.FirstOrDefault(o => o.FeatureID == TDSFeatureID.UserAgentSupport);
if (ack != null)
{
responseFound = true;
}
};

// Open the connection (this triggers the LOGIN7 exchange)
using var connection = new SqlConnection(server.ConnectionString);
connection.Open();

// Verify client did offer UserAgent
Assert.True(loginFound, "Expected UserAgent extension in LOGIN7");

// Verify server never acknowledged it
Assert.False(responseFound, "Server should not acknowledge UserAgent");

// Verify the connection itself succeeded
Assert.Equal(ConnectionState.Open, connection.State);
}

// Test to verify the driver behaviour even if server sends an Ack
[Fact]
public void TestConnWithAckedUserAgentFeatureExtension()
{
using var server = TestTdsServer.StartTestServer();

// Configure the test server
server.ServerSupportedUserAgentFeatureExtVersion = 0x01;
server.EnableUserAgentFeatureExt = true;

// Opt in to forced ACK for UserAgentSupport (no negotiation)
server.EmitUserAgentFeatureExtAck = true;

bool loginFound = false;
bool responseFound = false;

// Observe what the client sends in LOGIN7
server.OnLogin7Validated = loginToken =>
{
var token = loginToken.FeatureExt
.OfType<TDSLogin7GenericOptionToken>()
.FirstOrDefault(t => t.FeatureID == TDSFeatureID.UserAgentSupport);
if (token != null)
Comment thread
paulmedynski marked this conversation as resolved.
Outdated
{
Assert.Equal((byte)TDSFeatureID.UserAgentSupport, (byte)token.FeatureID);
loginFound = true;
}
};

// Verify the server sent back an ACK for UserAgentSupport
server.OnAuthenticationResponseCompleted = response =>
Comment thread
paulmedynski marked this conversation as resolved.
Outdated
{
// Find any FeatureExtAck option with FeatureID == UserAgentSupport
var uaAckOptions = response
.OfType<TDSFeatureExtAckToken>()
.SelectMany(t => t.Options)
.OfType<TDSFeatureExtAckGenericOption>()
.Where(o => o.FeatureID == TDSFeatureID.UserAgentSupport)
.ToList();

Assert.True(uaAckOptions.Count >= 1, "Expected an ACK for UserAgentSupport");
responseFound = true;
};

// Act: open the connection which triggers the LOGIN7 exchange
using var connection = new SqlConnection(server.ConnectionString);
connection.Open();

// Assert: client advertised the feature, server acknowledged it, connection is healthy
Assert.True(loginFound, "Expected UserAgent extension in LOGIN7");
Assert.True(responseFound, "Server should acknowledge UserAgent when forced");
Assert.Equal(ConnectionState.Open, connection.State);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,10 @@ public interface ITDSServerSession
/// Indicates whether the client supports Vector column type
/// </summary>
bool IsVectorSupportEnabled { get; set; }

/// <summary>
/// Indicates whether the client supports UserAgent Feature Extension
/// </summary>
bool IsUserAgentSupportEnabled { get; set; }
Comment thread
paulmedynski marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,41 @@ public delegate void OnAuthenticationCompletedDelegate(
/// </summary>
public bool EnableVectorFeatureExt { get; set; } = false;

/// <summary>
/// Property for enabling user agent feature extension.
/// </summary>
public bool EnableUserAgentFeatureExt { get; set; } = true;
Comment thread
paulmedynski marked this conversation as resolved.
Outdated

/// <summary>
/// Property for setting server version for vector feature extension.
/// </summary>
public byte ServerSupportedVectorFeatureExtVersion { get; set; } = DefaultSupportedVectorFeatureExtVersion;

/// <summary>
/// Property for setting server version for user agent feature extension.
/// </summary>
public byte ServerSupportedUserAgentFeatureExtVersion { get; set; } = DefaultSupportedUserAgentFeatureExtVersion;

/// <summary>
/// Client version for vector FeatureExtension.
/// </summary>
private byte _clientSupportedVectorFeatureExtVersion = 0;

/// <summary>
/// Client version for User Agent FeatureExtension.
/// </summary>
private byte _clientSupportedUserAgentFeatureExtVersion = 0;

/// <summary>
/// Server will ACK UserAgentSupport in the login response when this property is set to true.
/// </summary>
public bool EmitUserAgentFeatureExtAck { get; set; } = false;

/// <summary>
/// Default feature extension version supported on the server for user agent.
/// </summary>
public const byte DefaultSupportedUserAgentFeatureExtVersion = 0x01;

/// <summary>
/// Session counter
/// </summary>
Expand Down Expand Up @@ -287,7 +312,14 @@ public virtual TDSMessageCollection OnLogin7Request(ITDSServerSession session, T
}
break;
}

case TDSFeatureID.UserAgentSupport:
{
if (EnableUserAgentFeatureExt)
{
_clientSupportedUserAgentFeatureExtVersion = ((TDSLogin7GenericOptionToken)option).Data[0];
Comment thread
paulmedynski marked this conversation as resolved.
Outdated
}
break;
}
default:
{
// Do nothing
Expand Down Expand Up @@ -654,6 +686,31 @@ protected virtual TDSMessageCollection OnAuthenticationCompleted(ITDSServerSessi
}
}

// If tests request it, force an ACK for UserAgentSupport with no negotiation
if (EmitUserAgentFeatureExtAck)
{
byte ackVersion = ServerSupportedUserAgentFeatureExtVersion;

var data = new byte[] { ackVersion };
var uaAck = new TDSFeatureExtAckGenericOption(
TDSFeatureID.UserAgentSupport,
(uint)data.Length,
data);

// Reuse an existing FeatureExtAck token if present, otherwise add a new one
var featureExtAckToken = responseMessage.OfType<TDSFeatureExtAckToken>().FirstOrDefault();
if (featureExtAckToken == null)
{
featureExtAckToken = new TDSFeatureExtAckToken(uaAck);
responseMessage.Add(featureExtAckToken);
}
else
{
featureExtAckToken.Options.Add(uaAck);
}
}


// Create DONE token
Comment thread
paulmedynski marked this conversation as resolved.
Outdated
TDSDoneToken doneToken = new TDSDoneToken(TDSDoneTokenStatusType.Final);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ public class GenericTDSServerSession : ITDSServerSession
/// </summary>
public bool IsVectorSupportEnabled { get; set; }

/// <summary>
/// Indicates whether this session supports User Agent Feature Extension
/// </summary>
public bool IsUserAgentSupportEnabled { get; set; }
Comment thread
paulmedynski marked this conversation as resolved.

#region Session Options

/// <summary>
Expand Down
Loading
Loading