Skip to content

Commit 427047e

Browse files
committed
* Ensure all vhosts passed in a list of Uris match
1 parent 41d3098 commit 427047e

File tree

4 files changed

+74
-62
lines changed

4 files changed

+74
-62
lines changed

RabbitMQ.AMQP.Client/ConnectionSettings.cs

Lines changed: 52 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public class ConnectionSettings : IEquatable<ConnectionSettings>
128128
{
129129
private readonly Address _address;
130130
private readonly List<Address> _addresses = new();
131-
private readonly string _virtualHost = "/";
131+
private readonly string _virtualHost = Consts.DefaultVirtualHost;
132132
private readonly string _containerId = "";
133133
private readonly uint _maxFrameSize = Consts.DefaultMaxFrameSize;
134134
private readonly TlsSettings? _tlsSettings;
@@ -144,35 +144,9 @@ public class ConnectionSettings : IEquatable<ConnectionSettings>
144144
*/
145145
public ConnectionSettings(Uri uri)
146146
{
147-
string? user = null;
148-
string? password = null;
149-
string userInfo = uri.UserInfo;
150-
if (!string.IsNullOrEmpty(userInfo))
151-
{
152-
string[] userPass = userInfo.Split(':');
153-
if (userPass.Length > 2)
154-
{
155-
throw new ArgumentException($"Bad user info in AMQP URI: {userInfo}");
156-
}
147+
(string? user, string? password) = ProcessUserInfo(uri);
157148

158-
user = UriDecode(userPass[0]);
159-
if (userPass.Length == 2)
160-
{
161-
password = UriDecode(userPass[1]);
162-
}
163-
}
164-
165-
// C# automatically changes URIs into a canonical form
166-
// that has at least the path segment "/"
167-
if (uri.Segments.Length > 2)
168-
{
169-
throw new ArgumentException($"Multiple segments in path of AMQP URI: {string.Join(", ", uri.Segments)}");
170-
}
171-
172-
if (uri.Segments.Length == 2)
173-
{
174-
_virtualHost = UriDecode(uri.Segments[1]);
175-
}
149+
_virtualHost = ProcessUriSegmentsForVirtualHost(uri);
176150

177151
_address = new Address(host: uri.Host,
178152
port: uri.Port,
@@ -201,44 +175,18 @@ public ConnectionSettings(IEnumerable<Uri> uris)
201175

202176
foreach (Uri uri in uris)
203177
{
204-
string? user = null;
205-
string? password = null;
206-
string userInfo = uri.UserInfo;
207-
if (!string.IsNullOrEmpty(userInfo))
208-
{
209-
string[] userPass = userInfo.Split(':');
210-
if (userPass.Length > 2)
211-
{
212-
throw new ArgumentException($"Bad user info in AMQP URI: {userInfo}");
213-
}
178+
(string? user, string? password) = ProcessUserInfo(uri);
214179

215-
user = UriDecode(userPass[0]);
216-
if (userPass.Length == 2)
217-
{
218-
password = UriDecode(userPass[1]);
219-
}
220-
}
221-
222-
// C# automatically changes URIs into a canonical form
223-
// that has at least the path segment "/"
224-
if (uri.Segments.Length > 2)
180+
if (tmpVirtualHost is null)
225181
{
226-
throw new ArgumentException($"Multiple segments in path of AMQP URI: {string.Join(", ", uri.Segments)}");
182+
tmpVirtualHost = ProcessUriSegmentsForVirtualHost(uri);
227183
}
228-
229-
if (uri.Segments.Length == 2)
184+
else
230185
{
231-
if (tmpVirtualHost is null)
232-
{
233-
tmpVirtualHost = UriDecode(uri.Segments[1]);
234-
}
235-
else
186+
string thisVirtualHost = ProcessUriSegmentsForVirtualHost(uri);
187+
if (false == thisVirtualHost.Equals(tmpVirtualHost, StringComparison.InvariantCultureIgnoreCase))
236188
{
237-
string thisVirtualHost = UriDecode(uri.Segments[1]);
238-
if (false == thisVirtualHost.Equals(tmpVirtualHost, StringComparison.InvariantCultureIgnoreCase))
239-
{
240-
throw new ArgumentException($"All AMQP URIs must use the same virtual host. Expected '{tmpVirtualHost}', got '{thisVirtualHost}'");
241-
}
189+
throw new ArgumentException($"All AMQP URIs must use the same virtual host. Expected '{tmpVirtualHost}', got '{thisVirtualHost}'");
242190
}
243191
}
244192

@@ -393,6 +341,48 @@ private static string UriDecode(string str)
393341
{
394342
return Uri.UnescapeDataString(str.Replace("+", "%2B"));
395343
}
344+
345+
private static (string? user, string? password) ProcessUserInfo(Uri uri)
346+
{
347+
string? user = null;
348+
string? password = null;
349+
string userInfo = uri.UserInfo;
350+
if (!string.IsNullOrEmpty(userInfo))
351+
{
352+
string[] userPass = userInfo.Split(':');
353+
if (userPass.Length > 2)
354+
{
355+
throw new ArgumentException($"Bad user info in AMQP URI: {userInfo}");
356+
}
357+
358+
user = UriDecode(userPass[0]);
359+
if (userPass.Length == 2)
360+
{
361+
password = UriDecode(userPass[1]);
362+
}
363+
}
364+
365+
return (user, password);
366+
}
367+
368+
private static string ProcessUriSegmentsForVirtualHost(Uri uri)
369+
{
370+
// C# automatically changes URIs into a canonical form
371+
// that has at least the path segment "/"
372+
if (uri.Segments.Length > 2)
373+
{
374+
throw new ArgumentException($"Multiple segments in path of AMQP URI: {string.Join(", ", uri.Segments)}");
375+
}
376+
377+
if (uri.Segments.Length == 2)
378+
{
379+
return UriDecode(uri.Segments[1]);
380+
}
381+
else
382+
{
383+
return Consts.DefaultVirtualHost;
384+
}
385+
}
396386
}
397387

398388
public class TlsSettings

RabbitMQ.AMQP.Client/Consts.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,10 @@ public static class Consts
1616
/// <code>uint.MinValue</code> means "no limit"
1717
/// </summary>
1818
public const uint DefaultMaxFrameSize = uint.MinValue; // NOTE: Azure/amqpnetlite uses 256 * 1024
19+
20+
/// <summary>
21+
/// The default virtual host, <c>/</c>
22+
/// </summary>
23+
public const string DefaultVirtualHost = "/";
1924
}
2025
}

RabbitMQ.AMQP.Client/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ abstract RabbitMQ.AMQP.Client.Impl.AbstractLifeCycle.CloseAsync() -> System.Thre
33
abstract RabbitMQ.AMQP.Client.Impl.StreamOptions.Builder() -> RabbitMQ.AMQP.Client.IConsumerBuilder!
44
const RabbitMQ.AMQP.Client.Consts.Bindings = "bindings" -> string!
55
const RabbitMQ.AMQP.Client.Consts.DefaultMaxFrameSize = 0 -> uint
6+
const RabbitMQ.AMQP.Client.Consts.DefaultVirtualHost = "/" -> string!
67
const RabbitMQ.AMQP.Client.Consts.Exchanges = "exchanges" -> string!
78
const RabbitMQ.AMQP.Client.Consts.Key = "key" -> string!
89
const RabbitMQ.AMQP.Client.Consts.Messages = "messages" -> string!

Tests/ConnectionTests/ConnectionSettingsTests.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,22 @@ public void ConnectionSettingsViaUris()
150150
Assert.Equal(scheme, a2.Scheme);
151151
}
152152

153+
[Fact]
154+
public void ConnectionSettingsViaUrisThrowsWithDifferentVirtualHosts()
155+
{
156+
const string scheme = "amqps";
157+
const string host = "rabbitmq-host.foo.baz.com";
158+
string user = RandomString(10);
159+
string pass = RandomString(10);
160+
161+
var uri0 = new Uri($"{scheme}://{user}:{pass}@{host}:5671/foo");
162+
var uri1 = new Uri($"{scheme}://{user}:{pass}@{host}:5681/bar");
163+
var uri2 = new Uri($"{scheme}://{user}:{pass}@{host}:5691/foo");
164+
165+
List<Uri> uris = [uri0, uri1, uri2];
166+
Assert.ThrowsAny<ArgumentException>(() => new ConnectionSettings(uris));
167+
}
168+
153169
[Fact]
154170
public async Task RaiseErrorsIfTheParametersAreNotValid()
155171
{

0 commit comments

Comments
 (0)