Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,12 @@ internal class SqlConnectionInternal : DbConnectionInternal, IDisposable
// @TODO: Should be private and accessed via internal property
internal bool IsVectorSupportEnabled = false;

/// <summary>
/// Flag indicating whether enhanced routing is supported by the server.
/// </summary>
// @TODO: Should be private and accessed via internal property
internal bool IsEnhancedRoutingSupportEnabled = false;

// @TODO: This should be private
internal readonly SyncAsyncLock _parserLock = new SyncAsyncLock();

Expand Down Expand Up @@ -1725,6 +1731,27 @@ internal void OnFeatureExtAck(int featureId, byte[] data)

break;
}
case TdsEnums.FEATUREEXT_ENHANCEDROUTINGSUPPORT:
{
SqlClientEventSource.Log.TryAdvancedTraceEvent(
$"SqlInternalConnectionTds.OnFeatureExtAck | ADV | " +
$"Object ID {ObjectID}, " +
$"Received feature extension acknowledgement for ENHANCEDROUTINGSUPPORT");

if (data.Length != 1)
{
SqlClientEventSource.Log.TryTraceEvent(
$"SqlInternalConnectionTds.OnFeatureExtAck | ERR | " +
$"Object ID {ObjectID}, " +
$"Unknown token for ENHANCEDROUTINGSUPPORT");

throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream);
}

// A value of 1 indicates that the server supports the feature.
IsEnhancedRoutingSupportEnabled = data[0] == 1;
break;
}
case TdsEnums.FEATUREEXT_USERAGENT:
{
// Unexpected ack from server but we ignore it entirely
Expand Down Expand Up @@ -3071,6 +3098,7 @@ private void Login(
requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching;
requestedFeatures |= TdsEnums.FeatureExtension.JsonSupport;
requestedFeatures |= TdsEnums.FeatureExtension.VectorSupport;
requestedFeatures |= TdsEnums.FeatureExtension.EnhancedRoutingSupport;
requestedFeatures |= TdsEnums.FeatureExtension.UserAgent;

_parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, encrypt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ public enum EnvChangeType : byte
public const byte FEATUREEXT_SQLDNSCACHING = 0x0B;
public const byte FEATUREEXT_JSONSUPPORT = 0x0D;
public const byte FEATUREEXT_VECTORSUPPORT = 0x0E;
public const byte FEATUREEXT_ENHANCEDROUTINGSUPPORT = 0x0F;
public const byte FEATUREEXT_USERAGENT = 0x10;

[Flags]
Expand All @@ -253,6 +254,7 @@ public enum FeatureExtension : uint
SQLDNSCaching = 1 << (TdsEnums.FEATUREEXT_SQLDNSCACHING - 1),
JsonSupport = 1 << (TdsEnums.FEATUREEXT_JSONSUPPORT - 1),
VectorSupport = 1 << (TdsEnums.FEATUREEXT_VECTORSUPPORT - 1),
EnhancedRoutingSupport = 1 << (TdsEnums.FEATUREEXT_ENHANCEDROUTINGSUPPORT - 1),
UserAgent = 1 << (TdsEnums.FEATUREEXT_USERAGENT - 1)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9189,6 +9189,27 @@ internal int WriteVectorSupportFeatureRequest(bool write)
return len;
}

/// <summary>
/// Writes the Enhanced Routing Support feature request to the physical state object.
/// The request does not contain a payload.
/// </summary>
/// <param name="write">If true, writes the feature request to the physical state object.</param>
/// <returns>The length of the feature request in bytes.</returns>
internal int WriteEnhancedRoutingSupportFeatureRequest(bool write)
{
const int len = 5;

if (write)
{
// Write Feature ID
_physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_ENHANCEDROUTINGSUPPORT);
// We don't send any data
WriteInt(0, _physicalStateObj);
}

return len;
}

/// <summary>
/// Writes the User Agent feature request to the physical state
/// object. The request includes the feature ID, feature data length,
Expand Down Expand Up @@ -9587,6 +9608,11 @@ private int ApplyFeatureExData(TdsEnums.FeatureExtension requestedFeatures,
length += WriteVectorSupportFeatureRequest(write);
}

if ((requestedFeatures & TdsEnums.FeatureExtension.EnhancedRoutingSupport) != 0)
{
length += WriteEnhancedRoutingSupportFeatureRequest(write);
}

length++; // for terminator
if (write)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Linq;
using Microsoft.Data.SqlClient.Connection;
using Microsoft.SqlServer.TDS;
using Microsoft.SqlServer.TDS.Login7;
using Microsoft.SqlServer.TDS.Servers;
using Xunit;

namespace Microsoft.Data.SqlClient.UnitTests.SimulatedServerTests;

[Collection("SimulatedServerTests")]
public class FeatureExtensionNegotiationTests : IClassFixture<FeatureExtensionNegotiationTests.SimulatedServerFixture>
{
private TdsServer _server;
private string _connectionString;

public FeatureExtensionNegotiationTests(SimulatedServerFixture fixture)
{
_server = fixture.TdsServer;
SqlConnectionStringBuilder builder = new()
{
DataSource = $"localhost,{_server.EndPoint.Port}",
Encrypt = SqlConnectionEncryptOption.Optional,
// Disable connection pooling to avoid recycling a connection that has already negotiated feature extensions
Pooling = false
};
_connectionString = builder.ConnectionString;
}

[Fact]
public void EnhancedRouting_EnabledByServer_ShouldBeEnabled()
{
// Arrange
_server.EnableEnhancedRouting = FeatureExtensionEnablementTriState.Enabled;

bool clientFeatureExtensionFound = false;
_server.OnLogin7Validated = loginToken =>
{
var token = loginToken.FeatureExt
.OfType<TDSLogin7GenericOptionToken>()
.FirstOrDefault(t => t.FeatureID == TDSFeatureID.EnhancedRoutingSupport);


// Test should fail if no EnhancedRoutingSupport FE token is found
Assert.NotNull(token);

Assert.Equal((byte)TDSFeatureID.EnhancedRoutingSupport, (byte)token.FeatureID);

clientFeatureExtensionFound = true;
};

using SqlConnection sqlConnection = new(_connectionString);

// Act
sqlConnection.Open();


// Assert
Assert.True(clientFeatureExtensionFound);
Assert.True(((SqlConnectionInternal)sqlConnection.InnerConnection).IsEnhancedRoutingSupportEnabled);
}

[Fact]
public void EnhancedRouting_DisabledByServer_ShouldBeDisabled()
{
// Arrange
_server.EnableEnhancedRouting = FeatureExtensionEnablementTriState.Disabled;

using SqlConnection sqlConnection = new(_connectionString);

// Act
sqlConnection.Open();


// Assert
Assert.False(((SqlConnectionInternal)sqlConnection.InnerConnection).IsEnhancedRoutingSupportEnabled);
}

[Fact]
public void EnhancedRouting_NotAcknowledgedByServer_ShouldBeDisabled()
{
// Arrange
_server.EnableEnhancedRouting = FeatureExtensionEnablementTriState.DoNotAcknowledge;

using SqlConnection sqlConnection = new(_connectionString);

// Act
sqlConnection.Open();


// Assert
Assert.False(((SqlConnectionInternal)sqlConnection.InnerConnection).IsEnhancedRoutingSupportEnabled);
}

public class SimulatedServerFixture : IDisposable
{
public SimulatedServerFixture()
{
TdsServer = new TdsServer();
TdsServer.Start();
}

public void Dispose()
{
TdsServer.Dispose();
}

public TdsServer TdsServer { get; private set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,10 @@ public interface ITDSServerSession
/// Indicates whether the client supports UserAgent
/// </summary>
bool IsUserAgentSupportEnabled { get; set; }

/// <summary>
/// Indicates whether the client supports Enhanced Routing
/// </summary>
bool IsEnhancedRoutingSupportEnabled { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.


namespace Microsoft.SqlServer.TDS.Servers
{
public enum FeatureExtensionEnablementTriState
{
Disabled,
Enabled,
DoNotAcknowledge
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public delegate void OnAuthenticationCompletedDelegate(
/// </summary>
public bool EnableUserAgentFeatureExt { get; set; } = false;

/// <summary>
/// Property for setting server enhanced routing enablement state.
/// </summary>
public FeatureExtensionEnablementTriState EnableEnhancedRouting { get; set; } = FeatureExtensionEnablementTriState.Disabled;

/// <summary>
/// Property for setting server version for vector feature extension.
/// </summary>
Expand Down Expand Up @@ -339,6 +344,12 @@ public virtual TDSMessageCollection OnLogin7Request(ITDSServerSession session, T
break;
}

case TDSFeatureID.EnhancedRoutingSupport:
{
session.IsEnhancedRoutingSupportEnabled = true;
break;
}

default:
{
// Do nothing
Expand Down Expand Up @@ -645,6 +656,7 @@ protected virtual TDSMessageCollection OnAuthenticationCompleted(ITDSServerSessi
CheckJsonSupported(session, responseMessage);
CheckVectorSupport(session, responseMessage);
CheckUserAgentSupport(session, responseMessage);
CheckEnhancedRoutingSupport(session, responseMessage);

if (!string.IsNullOrEmpty(Arguments.FailoverPartner))
{
Expand Down Expand Up @@ -798,6 +810,48 @@ protected void CheckUserAgentSupport(ITDSServerSession session, TDSMessage respo

}

/// <summary>
/// Check if Enhanced Routing support is enabled
/// </summary>
/// <param name="session">Server session</param>
/// <param name="responseMessage">Response message</param>
protected void CheckEnhancedRoutingSupport(ITDSServerSession session, TDSMessage responseMessage)
{
if (session.IsEnhancedRoutingSupportEnabled &&
EnableEnhancedRouting != FeatureExtensionEnablementTriState.DoNotAcknowledge)
{
// Create ack data (1 byte: IsEnabled)
byte[] data = new byte[1];

if (EnableEnhancedRouting == FeatureExtensionEnablementTriState.Enabled)
{
data[0] = 1;
}
else
{
data[0] = 0;
}

// Create enhanced routing support as a generic feature extension option
TDSFeatureExtAckGenericOption enhancedRoutingSupportOption = new TDSFeatureExtAckGenericOption(TDSFeatureID.EnhancedRoutingSupport, (uint)data.Length, data);

// Look for feature extension token
TDSFeatureExtAckToken featureExtAckToken = (TDSFeatureExtAckToken)responseMessage.Where(t => t is TDSFeatureExtAckToken).FirstOrDefault();

if (featureExtAckToken == null)
{
// Create feature extension ack token
featureExtAckToken = new TDSFeatureExtAckToken(enhancedRoutingSupportOption);
responseMessage.Add(featureExtAckToken);
}
else
{
// Update the existing token
featureExtAckToken.Options.Add(enhancedRoutingSupportOption);
}
}
}

/// <summary>
/// Complete the Federated Login
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ public class GenericTdsServerSession : ITDSServerSession
/// </summary>
public bool IsUserAgentSupportEnabled { get; set; }

/// <summary>
/// Indicates whether this session supports enhanced routing
/// </summary>
public bool IsEnhancedRoutingSupportEnabled { get; set; }

#region Session Options

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,9 @@
<AssemblyName>Microsoft.SqlServer.TDS.Servers</AssemblyName>
<ProjectGuid>{978063D3-FBB5-4E10-8C45-67C90BE1B931}</ProjectGuid>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<IntermediateOutputPath>$(ObjFolder)$(Configuration).$(Platform)\$(AssemblyName)</IntermediateOutputPath>
<OutputPath>$(BinFolder)$(Configuration).$(Platform)\$(AssemblyName)</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="ApplicationIntentFilterType.cs" />
<Compile Include="AuthenticatingTdsServer.cs" />
<Compile Include="AuthenticatingTdsServerArguments.cs" />
<Compile Include="DateFormatString.cs" />
<Compile Include="FederatedAuthenticationNegativeTdsServer.cs" />
<Compile Include="FederatedAuthenticationNegativeTdsServerArguments.cs" />
<Compile Include="FederatedAuthenticationNegativeTdsScenarioType.cs" />
<Compile Include="GenericTdsServer.cs" />
<Compile Include="GenericTdsServerSession.cs" />
<Compile Include="QueryEngine.cs" />
<Compile Include="RoutingTdsServer.cs" />
<Compile Include="RoutingTdsServerArguments.cs" />
<Compile Include="ServerNameFilterType.cs" />
<Compile Include="TdsServerArguments.cs" />
<Compile Include="TdsServer.cs" />
<Compile Include="TransientTdsErrorTdsServer.cs" />
<Compile Include="TransientTdsErrorTdsServerArguments.cs" />
<Compile Include="TransientDelayTdsServer.cs" />
<Compile Include="TransientDelayTdsServerArguments.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(TestsPath)tools\TDS\TDS.EndPoint\TDS.EndPoint.csproj">
<Name>TDS.EndPoint</Name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public enum TDSFeatureID : byte
/// </summary>
VectorSupport = 0x0E,

/// <summary>
/// Enhanced Routing Support
/// </summary>
EnhancedRoutingSupport = 0x0F,

/// <summary>
/// User Agent Support
/// </summary>
Expand Down
Loading