SqlClient: parse optional Protocol part of the connection string Data Source (#1684)
* Add protocol support in connection strings * Add comments. Co-authored-by: Cijo Thomas <cithomas@microsoft.com>
This commit is contained in:
parent
4e4f95bb53
commit
2b3e2147cc
|
|
@ -30,13 +30,35 @@ namespace OpenTelemetry.Instrumentation.SqlClient
|
|||
{
|
||||
/*
|
||||
* Match...
|
||||
* protocol[ ]:[ ]serverName
|
||||
* serverName
|
||||
* serverName[ ]\\[ ]instanceName
|
||||
* serverName[ ]\[ ]instanceName
|
||||
* serverName[ ],[ ]port
|
||||
* serverName[ ]\\[ ]instanceName[ ],[ ]port
|
||||
* serverName[ ]\[ ]instanceName[ ],[ ]port
|
||||
*
|
||||
* [ ] can be any number of white-space, SQL allows it for some reason.
|
||||
*
|
||||
* Optional "protocol" can be "tcp", "lpc" (shared memory), or "np" (named pipes). See:
|
||||
* https://docs.microsoft.com/troubleshoot/sql/connect/use-server-name-parameter-connection-string, and
|
||||
* https://docs.microsoft.com/dotnet/api/system.data.sqlclient.sqlconnection.connectionstring?view=dotnet-plat-ext-5.0
|
||||
*
|
||||
* In case of named pipes the Data Source string can take form of:
|
||||
* np:serverName\instanceName, or
|
||||
* np:\\serverName\pipe\pipeName, or
|
||||
* np:\\serverName\pipe\MSSQL$instanceName\pipeName - in this case a separate regex (see NamedPipeRegex below)
|
||||
* is used to extract instanceName
|
||||
*/
|
||||
private static readonly Regex DataSourceRegex = new Regex("^(.*?)\\s*(?:[\\\\,]|$)\\s*(.*?)\\s*(?:,|$)\\s*(.*)$", RegexOptions.Compiled);
|
||||
private static readonly Regex DataSourceRegex = new Regex("^(.*\\s*:\\s*\\\\{0,2})?(.*?)\\s*(?:[\\\\,]|$)\\s*(.*?)\\s*(?:,|$)\\s*(.*)$", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// In a Data Source string like "np:\\serverName\pipe\MSSQL$instanceName\pipeName" match the
|
||||
/// "pipe\MSSQL$instanceName" segment to extract instanceName if it is available.
|
||||
/// </summary>
|
||||
/// <see>
|
||||
/// <a href="https://docs.microsoft.com/previous-versions/sql/sql-server-2016/ms189307(v=sql.130)"/>
|
||||
/// </see>
|
||||
private static readonly Regex NamedPipeRegex = new Regex("pipe\\\\MSSQL\\$(.*?)\\\\", RegexOptions.Compiled);
|
||||
|
||||
private static readonly ConcurrentDictionary<string, SqlConnectionDetails> ConnectionDetailCache = new ConcurrentDictionary<string, SqlConnectionDetails>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// .NET Framework implementation uses SqlEventSource from which we can't reliably distinguish
|
||||
|
|
@ -121,9 +143,11 @@ namespace OpenTelemetry.Instrumentation.SqlClient
|
|||
{
|
||||
Match match = DataSourceRegex.Match(dataSource);
|
||||
|
||||
string serverHostName = match.Groups[1].Value;
|
||||
string serverHostName = match.Groups[2].Value;
|
||||
string serverIpAddress = null;
|
||||
|
||||
string instanceName;
|
||||
|
||||
var uriHostNameType = Uri.CheckHostName(serverHostName);
|
||||
if (uriHostNameType == UriHostNameType.IPv4 || uriHostNameType == UriHostNameType.IPv6)
|
||||
{
|
||||
|
|
@ -131,25 +155,56 @@ namespace OpenTelemetry.Instrumentation.SqlClient
|
|||
serverHostName = null;
|
||||
}
|
||||
|
||||
string instanceName;
|
||||
string port;
|
||||
if (match.Groups[3].Length > 0)
|
||||
string maybeProtocol = match.Groups[1].Value;
|
||||
bool isNamedPipe = maybeProtocol.Length > 0 &&
|
||||
maybeProtocol.StartsWith("np", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (isNamedPipe)
|
||||
{
|
||||
instanceName = match.Groups[2].Value;
|
||||
port = match.Groups[3].Value;
|
||||
string pipeName = match.Groups[3].Value;
|
||||
if (pipeName.Length > 0)
|
||||
{
|
||||
var namedInstancePipeMatch = NamedPipeRegex.Match(pipeName);
|
||||
if (namedInstancePipeMatch.Success)
|
||||
{
|
||||
instanceName = namedInstancePipeMatch.Groups[1].Value;
|
||||
return new SqlConnectionDetails
|
||||
{
|
||||
ServerHostName = serverHostName,
|
||||
ServerIpAddress = serverIpAddress,
|
||||
InstanceName = instanceName,
|
||||
Port = null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return new SqlConnectionDetails
|
||||
{
|
||||
ServerHostName = serverHostName,
|
||||
ServerIpAddress = serverIpAddress,
|
||||
InstanceName = null,
|
||||
Port = null,
|
||||
};
|
||||
}
|
||||
|
||||
string port;
|
||||
if (match.Groups[4].Length > 0)
|
||||
{
|
||||
instanceName = match.Groups[3].Value;
|
||||
port = match.Groups[4].Value;
|
||||
if (port == "1433")
|
||||
{
|
||||
port = null;
|
||||
}
|
||||
}
|
||||
else if (int.TryParse(match.Groups[2].Value, out int parsedPort))
|
||||
else if (int.TryParse(match.Groups[3].Value, out int parsedPort))
|
||||
{
|
||||
port = parsedPort == 1433 ? null : match.Groups[2].Value;
|
||||
port = parsedPort == 1433 ? null : match.Groups[3].Value;
|
||||
instanceName = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
instanceName = match.Groups[2].Value;
|
||||
instanceName = match.Groups[3].Value;
|
||||
|
||||
if (string.IsNullOrEmpty(instanceName))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,6 +43,14 @@ namespace OpenTelemetry.Instrumentation.SqlClient.Tests
|
|||
[InlineData("127.0.0.1, 1818", null, "127.0.0.1", null, "1818")]
|
||||
[InlineData("127.0.0.1 \\ instanceName", null, "127.0.0.1", "instanceName", null)]
|
||||
[InlineData("127.0.0.1\\instanceName, 1818", null, "127.0.0.1", "instanceName", "1818")]
|
||||
[InlineData("tcp:127.0.0.1\\instanceName, 1818", null, "127.0.0.1", "instanceName", "1818")]
|
||||
[InlineData("tcp:localhost", "localhost", null, null, null)]
|
||||
[InlineData("tcp : localhost", "localhost", null, null, null)]
|
||||
[InlineData("np : localhost", "localhost", null, null, null)]
|
||||
[InlineData("lpc:localhost", "localhost", null, null, null)]
|
||||
[InlineData("np:\\\\localhost\\pipe\\sql\\query", "localhost", null, null, null)]
|
||||
[InlineData("np : \\\\localhost\\pipe\\sql\\query", "localhost", null, null, null)]
|
||||
[InlineData("np:\\\\localhost\\pipe\\MSSQL$instanceName\\sql\\query", "localhost", null, "instanceName", null)]
|
||||
public void ParseDataSourceTests(
|
||||
string dataSource,
|
||||
string expectedServerHostName,
|
||||
|
|
|
|||
Loading…
Reference in New Issue