Skip to content
Merged
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 @@ -88,21 +88,38 @@ public static NatsInstrumentationOptions FilterSubjects(
}

// NATS subject match: '*' matches exactly one token, '>' matches one or more trailing tokens.
// Walks the subject token by token instead of allocating a string[] per message; the pattern is
// already tokenized once at configuration time.
private static bool Matches(string subject, string[] pattern)
{
var tokens = subject.Split('.');
var remaining = subject.AsSpan();
var exhausted = false;

for (var i = 0; i < pattern.Length; i++)
{
if (pattern[i] == ">")
return tokens.Length > i;
return !exhausted; // '>' needs at least one remaining subject token; exhausted means the subject is fully consumed

if (i >= tokens.Length)
return false;
if (exhausted)
return false; // pattern has more tokens than the subject

var dot = remaining.IndexOf('.');
ReadOnlySpan<char> token;
if (dot < 0)
{
token = remaining;
exhausted = true; // this was the subject's last token
}
else
{
token = remaining.Slice(0, dot);
remaining = remaining.Slice(dot + 1);
}

if (pattern[i] != "*" && !string.Equals(pattern[i], tokens[i], StringComparison.Ordinal))
if (pattern[i] != "*" && !pattern[i].AsSpan().SequenceEqual(token))
return false;
}

return tokens.Length == pattern.Length;
return exhausted; // subject and pattern must have the same token count
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,32 @@ public void FilterSubjects_composes_with_existing_filter()
options.Filter!(Context("payments.new")).Should().BeFalse(); // dropped by the pre-existing filter
}

// Trailing dots are not valid in NATS subjects; this pins the matcher to the same
// behavior as the previous Split-based implementation, not protocol compliance.
[Theory]
[InlineData("foo.", true)] // trailing dot: tokens are "foo" and "", '*' matches the empty token
Comment thread
mtmk marked this conversation as resolved.
[InlineData("foo", false)] // only one token, pattern needs two
[InlineData("foo.bar.", false)] // three tokens ("foo", "bar", ""), pattern needs exactly two
public void FilterSubjects_invalid_subject_empty_trailing_token(string subject, bool expected)
{
var options = new NatsInstrumentationOptions().FilterSubjects(include: ["foo.*"]);

options.Filter!(Context(subject)).Should().Be(expected);
}

// Consecutive dots are not valid in NATS subjects; this pins the matcher to the same
// behavior as the previous Split-based implementation, not protocol compliance.
[Theory]
[InlineData("foo..bar", true)] // consecutive dots: tokens "foo", "", "bar"
[InlineData("foo.bar", true)]
[InlineData("foo", false)] // '>' needs at least one trailing token
public void FilterSubjects_invalid_subject_greater_than_spans_empty_tokens(string subject, bool expected)
{
var options = new NatsInstrumentationOptions().FilterSubjects(include: ["foo.>"]);

options.Filter!(Context(subject)).Should().Be(expected);
}

[Fact]
public void FilterSubjects_without_patterns_leaves_filter_unset()
{
Expand Down
Loading