Skip to content

Commit 25306a4

Browse files
Enable User Agent Extension (#3606)
1 parent ec18bab commit 25306a4

File tree

11 files changed

+378
-47
lines changed

11 files changed

+378
-47
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,6 @@ internal bool IsDNSCachingBeforeRedirectSupported
210210
// Json Support Flag
211211
internal bool IsJsonSupportEnabled = false;
212212

213-
// User Agent Flag
214-
internal bool IsUserAgentEnabled = true;
215-
216213
// Vector Support Flag
217214
internal bool IsVectorSupportEnabled = false;
218215

@@ -1414,10 +1411,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword,
14141411
requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching;
14151412
requestedFeatures |= TdsEnums.FeatureExtension.JsonSupport;
14161413
requestedFeatures |= TdsEnums.FeatureExtension.VectorSupport;
1417-
1418-
#if DEBUG
14191414
requestedFeatures |= TdsEnums.FeatureExtension.UserAgent;
1420-
#endif
14211415

14221416
_parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, encrypt);
14231417
}
@@ -3023,6 +3017,12 @@ internal void OnFeatureExtAck(int featureId, byte[] data)
30233017
IsVectorSupportEnabled = true;
30243018
break;
30253019
}
3020+
case TdsEnums.FEATUREEXT_USERAGENT:
3021+
{
3022+
// Unexpected ack from server but we ignore it entirely
3023+
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.SqlInternalConnectionTds.OnFeatureExtAck|ADV> {0}, Received feature extension acknowledgement for USERAGENTSUPPORT (ignored)", ObjectID);
3024+
break;
3025+
}
30263026

30273027
default:
30283028
{

src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,6 @@ internal bool IsDNSCachingBeforeRedirectSupported
214214
// Vector Support Flag
215215
internal bool IsVectorSupportEnabled = false;
216216

217-
// User Agent Flag
218-
internal bool IsUserAgentEnabled = true;
219-
220217
// TCE flags
221218
internal byte _tceVersionSupported;
222219

@@ -1423,9 +1420,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword,
14231420
requestedFeatures |= TdsEnums.FeatureExtension.JsonSupport;
14241421
requestedFeatures |= TdsEnums.FeatureExtension.VectorSupport;
14251422

1426-
#if DEBUG
14271423
requestedFeatures |= TdsEnums.FeatureExtension.UserAgent;
1428-
#endif
14291424

14301425
_parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, encrypt);
14311426
}
@@ -3068,7 +3063,18 @@ internal void OnFeatureExtAck(int featureId, byte[] data)
30683063
IsVectorSupportEnabled = true;
30693064
break;
30703065
}
3066+
case TdsEnums.FEATUREEXT_USERAGENT:
3067+
{
3068+
// TODO: Verify that the server sends an acknowledgment (Ack)
3069+
// using this log message in the future.
30713070

3071+
// This Ack from the server is unexpected and is ignored completely.
3072+
// According to the TDS specification, an Ack is not defined/expected
3073+
// for this scenario. We handle it only for completeness
3074+
// and to support testing.
3075+
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.SqlInternalConnectionTds.OnFeatureExtAck|ADV> {0}, Received feature extension acknowledgement for USERAGENTSUPPORT (ignored)", ObjectID);
3076+
break;
3077+
}
30723078
default:
30733079
{
30743080
// Unknown feature ack

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ private enum Tristate : byte
2525
private const string UseConnectionPoolV2String = @"Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2";
2626
private const string TruncateScaledDecimalString = @"Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal";
2727
private const string IgnoreServerProvidedFailoverPartnerString = @"Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner";
28+
private const string EnableUserAgentString = @"Switch.Microsoft.Data.SqlClient.EnableUserAgent";
2829
#if NET
2930
private const string GlobalizationInvariantModeString = @"System.Globalization.Invariant";
3031
private const string GlobalizationInvariantModeEnvironmentVariable = "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT";
@@ -45,6 +46,7 @@ private enum Tristate : byte
4546
private static Tristate s_useConnectionPoolV2;
4647
private static Tristate s_truncateScaledDecimal;
4748
private static Tristate s_ignoreServerProvidedFailoverPartner;
49+
private static Tristate s_enableUserAgent;
4850
#if NET
4951
private static Tristate s_globalizationInvariantMode;
5052
private static Tristate s_useManagedNetworking;
@@ -328,7 +330,27 @@ public static bool IgnoreServerProvidedFailoverPartner
328330
return s_ignoreServerProvidedFailoverPartner == Tristate.True;
329331
}
330332
}
331-
333+
/// <summary>
334+
/// When set to true, the user agent feature is enabled and the driver will send the user agent string to the server.
335+
/// </summary>
336+
public static bool EnableUserAgent
337+
{
338+
get
339+
{
340+
if (s_enableUserAgent == Tristate.NotInitialized)
341+
{
342+
if (AppContext.TryGetSwitch(EnableUserAgentString, out bool returnedValue) && returnedValue)
343+
{
344+
s_enableUserAgent = Tristate.True;
345+
}
346+
else
347+
{
348+
s_enableUserAgent = Tristate.False;
349+
}
350+
}
351+
return s_enableUserAgent == Tristate.True;
352+
}
353+
}
332354
#if NET
333355
/// <summary>
334356
/// .NET Core 2.0 and up supports Globalization Invariant mode, which reduces the size of the required libraries for

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,7 @@ public enum EnvChangeType : byte
241241
public const byte FEATUREEXT_SQLDNSCACHING = 0x0B;
242242
public const byte FEATUREEXT_JSONSUPPORT = 0x0D;
243243
public const byte FEATUREEXT_VECTORSUPPORT = 0x0E;
244-
// TODO: re-verify if this byte competes with another feature
245-
public const byte FEATUREEXT_USERAGENT = 0x0F;
244+
public const byte FEATUREEXT_USERAGENT = 0x10;
246245

247246
[Flags]
248247
public enum FeatureExtension : uint

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
using Microsoft.Data.SqlClient.DataClassification;
3030
using Microsoft.Data.SqlClient.LocalDb;
3131
using Microsoft.Data.SqlClient.Server;
32+
using Microsoft.Data.SqlClient.UserAgent;
3233
using Microsoft.Data.SqlClient.Utilities;
34+
35+
3336
#if NETFRAMEWORK
3437
using Microsoft.Data.SqlTypes;
3538
#endif
@@ -1361,7 +1364,14 @@ internal void TdsLogin(
13611364

13621365
int feOffset = length;
13631366
// calculate and reserve the required bytes for the featureEx
1364-
length = ApplyFeatureExData(requestedFeatures, recoverySessionData, fedAuthFeatureExtensionData, useFeatureExt, length);
1367+
length = ApplyFeatureExData(
1368+
requestedFeatures,
1369+
recoverySessionData,
1370+
fedAuthFeatureExtensionData,
1371+
UserAgentInfo.UserAgentCachedJsonPayload.ToArray(),
1372+
useFeatureExt,
1373+
length
1374+
);
13651375

13661376
WriteLoginData(rec,
13671377
requestedFeatures,
@@ -9448,7 +9458,15 @@ private void WriteLoginData(SqlLogin rec,
94489458
}
94499459
}
94509460

9451-
ApplyFeatureExData(requestedFeatures, recoverySessionData, fedAuthFeatureExtensionData, useFeatureExt, length, true);
9461+
ApplyFeatureExData(
9462+
requestedFeatures,
9463+
recoverySessionData,
9464+
fedAuthFeatureExtensionData,
9465+
UserAgentInfo.UserAgentCachedJsonPayload.ToArray(),
9466+
useFeatureExt,
9467+
length,
9468+
true
9469+
);
94529470
}
94539471
catch (Exception e)
94549472
{
@@ -9467,6 +9485,7 @@ private void WriteLoginData(SqlLogin rec,
94679485
private int ApplyFeatureExData(TdsEnums.FeatureExtension requestedFeatures,
94689486
SessionData recoverySessionData,
94699487
FederatedAuthenticationFeatureExtensionData fedAuthFeatureExtensionData,
9488+
byte[] userAgentJsonPayload,
94709489
bool useFeatureExt,
94719490
int length,
94729491
bool write = false)
@@ -9475,6 +9494,11 @@ private int ApplyFeatureExData(TdsEnums.FeatureExtension requestedFeatures,
94759494
{
94769495
checked
94779496
{
9497+
// NOTE: As part of TDS spec UserAgent feature extension should be the first feature extension in the list.
9498+
if (LocalAppContextSwitches.EnableUserAgent && ((requestedFeatures & TdsEnums.FeatureExtension.UserAgent) != 0))
9499+
{
9500+
length += WriteUserAgentFeatureRequest(userAgentJsonPayload, write);
9501+
}
94789502
if ((requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) != 0)
94799503
{
94809504
length += WriteSessionRecoveryFeatureRequest(recoverySessionData, write);

src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
3232
private readonly PropertyInfo _useConnectionPoolV2Property;
3333
private readonly PropertyInfo _truncateScaledDecimalProperty;
3434
private readonly PropertyInfo _ignoreServerProvidedFailoverPartner;
35+
private readonly PropertyInfo _enableUserAgent;
3536
#if NET
3637
private readonly PropertyInfo _globalizationInvariantModeProperty;
3738
private readonly PropertyInfo _useManagedNetworkingProperty;
@@ -60,6 +61,8 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
6061
private readonly Tristate _truncateScaledDecimalOriginal;
6162
private readonly FieldInfo _ignoreServerProvidedFailoverPartnerField;
6263
private readonly Tristate _ignoreServerProvidedFailoverPartnerOriginal;
64+
private readonly FieldInfo _enableUserAgentField;
65+
private readonly Tristate _enableUserAgentOriginal;
6366
#if NET
6467
private readonly FieldInfo _globalizationInvariantModeField;
6568
private readonly Tristate _globalizationInvariantModeOriginal;
@@ -162,7 +165,11 @@ void InitProperty(string name, out PropertyInfo property)
162165
"IgnoreServerProvidedFailoverPartner",
163166
out _ignoreServerProvidedFailoverPartner);
164167

165-
#if NET
168+
InitProperty(
169+
"EnableUserAgent",
170+
out _enableUserAgent);
171+
172+
#if NET
166173
InitProperty(
167174
"GlobalizationInvariantMode",
168175
out _globalizationInvariantModeProperty);
@@ -240,6 +247,11 @@ void InitField(string name, out FieldInfo field, out Tristate value)
240247
"s_ignoreServerProvidedFailoverPartner",
241248
out _ignoreServerProvidedFailoverPartnerField,
242249
out _ignoreServerProvidedFailoverPartnerOriginal);
250+
251+
InitField(
252+
"s_enableUserAgent",
253+
out _enableUserAgentField,
254+
out _enableUserAgentOriginal);
243255

244256
#if NET
245257
InitField(
@@ -323,6 +335,10 @@ void RestoreField(FieldInfo field, Tristate value)
323335
_ignoreServerProvidedFailoverPartnerField,
324336
_ignoreServerProvidedFailoverPartnerOriginal);
325337

338+
RestoreField(
339+
_enableUserAgentField,
340+
_enableUserAgentOriginal);
341+
326342
#if NET
327343
RestoreField(
328344
_globalizationInvariantModeField,
@@ -429,6 +445,11 @@ public bool IgnoreServerProvidedFailoverPartner
429445
get => (bool)_ignoreServerProvidedFailoverPartner.GetValue(null);
430446
}
431447

448+
public bool EnableUserAgent
449+
{
450+
get => (bool)_enableUserAgent.GetValue(null);
451+
}
452+
432453
#if NET
433454
/// <summary>
434455
/// Access the LocalAppContextSwitches.GlobalizationInvariantMode property.
@@ -553,6 +574,12 @@ public Tristate IgnoreServerProvidedFailoverPartnerField
553574
set => SetValue(_ignoreServerProvidedFailoverPartnerField, value);
554575
}
555576

577+
public Tristate EnableUserAgentField
578+
{
579+
get => GetValue(_enableUserAgentField);
580+
set => SetValue(_enableUserAgentField, value);
581+
}
582+
556583
#if NET
557584
/// <summary>
558585
/// Get or set the LocalAppContextSwitches.GlobalizationInvariantMode switch value.

0 commit comments

Comments
 (0)