Skip to content

Commit 4fb8553

Browse files
committed
Support community redirection protocol. Fixes #945
1 parent ff1a5fe commit 4fb8553

File tree

2 files changed

+61
-10
lines changed

2 files changed

+61
-10
lines changed

src/MySqlConnector/Utilities/Utility.cs

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -335,26 +335,58 @@ public static bool TryParseRedirectionHeader(string header, out string host, out
335335
port = 0;
336336
user = "";
337337

338-
if (!header.StartsWith("Location: mysql://", StringComparison.Ordinal))
338+
if (!header.StartsWith("Location: mysql://", StringComparison.Ordinal) || header.Length < 22)
339339
return false;
340340

341-
var hostIndex = 18;
342-
var colonIndex = header.IndexOf(':', hostIndex);
343-
if (colonIndex == -1)
344-
return false;
341+
bool isCommunityFormat;
342+
int portIndex;
343+
if (header[18] == '[')
344+
{
345+
// Community protocol:
346+
// Location: mysql://[redirectedHostName]:redirectedPort/?user=redirectedUser&ttl=%d\n
347+
isCommunityFormat = true;
348+
349+
var hostIndex = 19;
350+
var closeSquareBracketIndex = header.IndexOf(']', hostIndex);
351+
if (closeSquareBracketIndex == -1)
352+
return false;
353+
354+
host = header.Substring(hostIndex, closeSquareBracketIndex - hostIndex);
355+
if (header.Length <= closeSquareBracketIndex + 2)
356+
return false;
357+
if (header[closeSquareBracketIndex + 1] != ':')
358+
return false;
359+
portIndex = closeSquareBracketIndex + 2;
360+
}
361+
else
362+
{
363+
// Azure protocol:
364+
// Location: mysql://redirectedHostName:redirectedPort/user=redirectedUser&ttl=%d (where ttl is optional)
365+
isCommunityFormat = false;
345366

346-
host = header.Substring(hostIndex, colonIndex - hostIndex);
347-
var portIndex = colonIndex + 1;
348-
var userIndex = header.IndexOf("/user=", StringComparison.Ordinal);
367+
var hostIndex = 18;
368+
var colonIndex = header.IndexOf(':', hostIndex);
369+
if (colonIndex == -1)
370+
return false;
371+
372+
host = header.Substring(hostIndex, colonIndex - hostIndex);
373+
portIndex = colonIndex + 1;
374+
}
375+
376+
var userIndex = header.IndexOf(isCommunityFormat ? "/?user=" : "/user=", StringComparison.Ordinal);
349377
if (userIndex == -1)
350378
return false;
351379

352380
if (!int.TryParse(header.Substring(portIndex, userIndex - portIndex), out port) || port <= 0)
353381
return false;
354382

383+
userIndex += isCommunityFormat ? 7 : 6;
355384
var ampersandIndex = header.IndexOf('&', userIndex);
356-
user = ampersandIndex == -1 ? header.Substring(userIndex + 6) : header.Substring(userIndex + 6, ampersandIndex - userIndex - 6);
357-
return true;
385+
var newlineIndex = header.IndexOf('\n', userIndex);
386+
var terminatorIndex = ampersandIndex == -1 ? (newlineIndex == -1 ? header.Length : newlineIndex) :
387+
(newlineIndex == -1 ? ampersandIndex : Math.Min(ampersandIndex, newlineIndex));
388+
user = header.Substring(userIndex, terminatorIndex - userIndex);
389+
return user.Length != 0;
358390
}
359391

360392
public static TimeSpan ParseTimeSpan(ReadOnlySpan<byte> value)

tests/MySqlConnector.Tests/UtilityTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,17 @@ public class UtilityTests
1111
{
1212
[Theory]
1313
[InlineData("Location: mysql://host.example.com:1234/user=user@host", "host.example.com", 1234, "user@host")]
14+
[InlineData("Location: mysql://host.example.com:1234/user=user@host\n", "host.example.com", 1234, "user@host")]
1415
[InlineData("Location: mysql://host.example.com:1234/user=user@host&ttl=60", "host.example.com", 1234, "user@host")]
16+
[InlineData("Location: mysql://host.example.com:1234/user=user@host&ttl=60\n", "host.example.com", 1234, "user@host")]
17+
[InlineData("Location: mysql://[host.example.com]:1234/?user=abcd", "host.example.com", 1234, "abcd")]
18+
[InlineData("Location: mysql://[host.example.com]:1234/?user=abcd\n", "host.example.com", 1234, "abcd")]
19+
[InlineData("Location: mysql://[host.example.com]:1234/?user=abcd&ttl=60", "host.example.com", 1234, "abcd")]
20+
[InlineData("Location: mysql://[host.example.com]:1234/?user=abcd&ttl=60\n", "host.example.com", 1234, "abcd")]
21+
[InlineData("Location: mysql://[2001:4860:4860::8888]:1234/?user=abcd", "2001:4860:4860::8888", 1234, "abcd")]
22+
[InlineData("Location: mysql://[2001:4860:4860::8888]:1234/?user=abcd\n", "2001:4860:4860::8888", 1234, "abcd")]
23+
[InlineData("Location: mysql://[2001:4860:4860::8888]:1234/?user=abcd&ttl=60", "2001:4860:4860::8888", 1234, "abcd")]
24+
[InlineData("Location: mysql://[2001:4860:4860::8888]:1234/?user=abcd&ttl=60\n", "2001:4860:4860::8888", 1234, "abcd")]
1525
public void ParseRedirectionHeader(string input, string expectedHost, int expectedPort, string expectedUser)
1626
{
1727
Assert.True(Utility.TryParseRedirectionHeader(input, out var host, out var port, out var user));
@@ -24,12 +34,21 @@ public void ParseRedirectionHeader(string input, string expectedHost, int expect
2434
[InlineData("")]
2535
[InlineData("Location: mysql")]
2636
[InlineData("Location: mysql://host.example.com")]
37+
[InlineData("Location: mysql://host.example.com:")]
38+
[InlineData("Location: mysql://[host.example.com")]
39+
[InlineData("Location: mysql://[host.example.com]")]
40+
[InlineData("Location: mysql://[host.example.com]:")]
2741
[InlineData("Location: mysql://host.example.com:123")]
2842
[InlineData("Location: mysql://host.example.com:123/")]
43+
[InlineData("Location: mysql://[host.example.com]:123")]
44+
[InlineData("Location: mysql://[host.example.com]:123/")]
2945
[InlineData("Location: mysql://host.example.com:/user=")]
46+
[InlineData("Location: mysql://host.example.com:123/user=")]
47+
[InlineData("Location: mysql://[host.example.com]:123/?user=")]
3048
[InlineData("Location: mysql://host.example.com:/user=user@host")]
3149
[InlineData("Location: mysql://host.example.com:-1/user=user@host")]
3250
[InlineData("Location: mysql://host.example.com:0/user=user@host")]
51+
[InlineData("Location: mysql://[host.example.com]:123/user=abcd")]
3352
public void ParseRedirectionHeaderFails(string input)
3453
{
3554
Assert.False(Utility.TryParseRedirectionHeader(input, out _, out _, out _));

0 commit comments

Comments
 (0)