Skip to content

Add bridge exclusion#1479

Open
NiklasCi wants to merge 1 commit intonanomq:mainfrom
NiklasCi:enhancement/add-bridge-exclusion
Open

Add bridge exclusion#1479
NiklasCi wants to merge 1 commit intonanomq:mainfrom
NiklasCi:enhancement/add-bridge-exclusion

Conversation

@NiklasCi
Copy link
Copy Markdown

@NiklasCi NiklasCi commented Apr 1, 2026

Recreated after i completly destroid #1464. Git submodules are such fun.

This pull request introduces support for "exclusions" in the bridge configuration, allowing specific topics to be excluded from bridge operations. The changes include updates to the bridge node structure, parsing logic, cleanup routines, and configuration printing to handle exclusions alongside existing forwards and subscriptions.

Bridge exclusions support:

  • Added a new exclusions struct and corresponding exclusions_list and exclusions_count fields to the conf_bridge_node structure in conf.h, enabling storage of excluded topics. [1] [2]
  • Updated the bridge node parsing function (conf_bridge_node_parse) to read and validate the exclusions array from the configuration, allocate exclusion entries, and track their count.
  • Enhanced the bridge node destruction logic (conf_bridge_node_destroy) to properly free all allocated exclusions and their topics, preventing memory leaks.
  • Modified the configuration printing function (print_bridge_conf) to display the exclusions for each bridge node, improving debuggability and transparency.

Minor code cleanup:

  • Removed an unnecessary blank line in the authorization parsing function for consistency.

Summary by CodeRabbit

Release Notes

  • New Features
    • Added support for topic exclusions in bridge node configuration, allowing users to specify topics that should be excluded from bridge forwarding operations.

Signed-off-by: Niklas Ciecior <niklas.ciecior@tricera.energy>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

The changes introduce support for topic exclusions in bridge node configurations. A new exclusions struct type is added to represent excluded topics, and the conf_bridge_node struct is extended to store a list of exclusions. Implementation includes cleanup logic in destructors, parsing and validation in configuration handlers, and logging functionality.

Changes

Cohort / File(s) Summary
Structure Definitions
include/nng/supplemental/nanolib/conf.h
Added new exclusions typedef with topic pointer and topic_len field. Extended conf_bridge_node struct with exclusions_count and exclusions_list members.
Cleanup and Display Logic
src/supplemental/nanolib/conf.c
Updated conf_bridge_node_destroy() to free exclusions_list and each element's topic field. Extended print_bridge_conf() to log exclusions section and adjusted spacing in existing bridge configuration output.
Configuration Parsing
src/supplemental/nanolib/conf_ver2.c
Added parsing logic in conf_bridge_node_parse() to read exclusions list from config, validate topic entries, and populate the exclusions_list. Removed whitespace in conf_authorization_prase_ver2().

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 bounces through code with delight
Topic exclusions, what a sight!
Configuration bridges dance just right,
No unwanted MQTT in our light,
Exclusions make everything bright! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add bridge exclusion' directly and clearly summarizes the main change: introducing exclusion support to bridge configuration.
Description check ✅ Passed The description provides comprehensive details about the changes, their purpose, and affected functions, though it does not follow the template's required format with issue number and commit message structure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
include/nng/supplemental/nanolib/conf.h (1)

227-227: Type inconsistency: topic_len uses size_t while similar fields use uint32_t.

The topics struct (lines 230-247) uses uint32_t for remote_topic_len and local_topic_len, but the new exclusions struct uses size_t for topic_len. This inconsistency could lead to issues on platforms where size_t differs from uint32_t, and it makes the codebase less uniform.

♻️ Suggested fix for type consistency
 typedef struct {
 	char       *topic;
-	size_t 		  topic_len;
+	uint32_t    topic_len;
 } exclusions;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/nng/supplemental/nanolib/conf.h` at line 227, The new exclusions
struct introduces topic_len as size_t which is inconsistent with the topics
struct fields remote_topic_len and local_topic_len that use uint32_t; change
topic_len to uint32_t to match existing types and avoid platform-dependent size
differences—update the declaration of topic_len in the exclusions struct and
verify any code that reads/writes exclusions->topic_len uses uint32_t
(referencing the exclusions struct and topics' remote_topic_len/local_topic_len
symbols to locate all related usages).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@include/nng/supplemental/nanolib/conf.h`:
- Around line 225-228: The exclusions parsed into the exclusions struct
(exclusions { char *topic; size_t topic_len; }) and stored in exclusions_list
are never consulted during bridge forwarding; update the bridge
message-forwarding path (where messages are forwarded across the bridge) to
iterate the exclusions_list and skip forwarding when a message's topic matches
any exclusions[i].topic using topic_len-aware comparison (memcmp or strncmp) or
exact string compare as appropriate; ensure you use the same lifetime/ownership
semantics as the parser (don't free entries when skipping) and add unit/inline
checks so that the forwarding function returns early when a match is found.

In `@src/supplemental/nanolib/conf_ver2.c`:
- Around line 1267-1282: The loop over exclusions_list currently breaks on an
invalid empty/null topic which aborts processing remaining exclusions; change
the behavior in the cJSON_ArrayForEach(exclusion, exclusions_list) loop so that
after hocon_read_str(exc, topic, exclusion) you validate topic_len and on
invalid topic call log_error, free the allocated struct (NNI_FREE_STRUCT(exc))
and continue (not break) to skip only that entry; additionally add the same
maximum-topic-length check used in forwards/subscription parsing (compare
topic_len against the established MAX_TOPIC_LEN constant or the same validation
routine), free exc and continue if it exceeds the max, otherwise set
exc->topic_len and cvector_push_back(node->exclusions_list, exc); finally ensure
node->exclusions_count = cvector_size(node->exclusions_list) remains after the
loop.

In `@src/supplemental/nanolib/conf.c`:
- Around line 3770-3776: The format string is unsafe: %.*s expects an int
precision and %ld expects a long, but topic_len is size_t and j is size_t;
update the logging call in the loop that uses node->exclusions_count,
node->exclusions_list and node->exclusions_list[j]->topic_len so the types match
the format specifiers—cast topic_len to int for the precision argument (e.g.,
(int)node->exclusions_list[j]->topic_len) and either switch %ld to %zu for j or
cast j to long (prefer %zu to match size_t); adjust the log_info invocation
accordingly.

---

Nitpick comments:
In `@include/nng/supplemental/nanolib/conf.h`:
- Line 227: The new exclusions struct introduces topic_len as size_t which is
inconsistent with the topics struct fields remote_topic_len and local_topic_len
that use uint32_t; change topic_len to uint32_t to match existing types and
avoid platform-dependent size differences—update the declaration of topic_len in
the exclusions struct and verify any code that reads/writes
exclusions->topic_len uses uint32_t (referencing the exclusions struct and
topics' remote_topic_len/local_topic_len symbols to locate all related usages).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c4457307-5452-49cb-88ee-439d90d47c37

📥 Commits

Reviewing files that changed from the base of the PR and between 5ea4e82 and 6bb8d9a.

📒 Files selected for processing (3)
  • include/nng/supplemental/nanolib/conf.h
  • src/supplemental/nanolib/conf.c
  • src/supplemental/nanolib/conf_ver2.c

Comment on lines +225 to +228
typedef struct {
char *topic;
size_t topic_len;
} exclusions;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for usages of exclusions_list or exclusions_count beyond parsing/printing/cleanup

# Search for exclusions usage in the codebase
rg -n "exclusions" --type=c -C3 | grep -v "conf_ver2.c" | grep -v "conf\.c" | grep -v "conf\.h"

Repository: nanomq/NanoNNG

Length of output: 72


🏁 Script executed:

# Search for all exclusions usage without filters to see full picture
rg -n "exclusions" --type=c -B2 -A2

Repository: nanomq/NanoNNG

Length of output: 3934


🏁 Script executed:

# Search for message filtering/forwarding logic that might use exclusions_list
rg -n "forward|filter" --type=c -l | head -20

Repository: nanomq/NanoNNG

Length of output: 654


🏁 Script executed:

# Check the conf.c file specifically for exclusions usage and filtering logic
fd -e c -e h "^conf\.c$" -x cat -n {}

Repository: nanomq/NanoNNG

Length of output: 50371


🏁 Script executed:

# Search for bridge message forwarding/processing functions
rg -n "forward|bridge" --type=c -l | grep -E "bridge|mqtt" | head -20

Repository: nanomq/NanoNNG

Length of output: 462


🏁 Script executed:

# Search for any use of exclusions_list for filtering in the codebase
rg -n "exclusions_list\[" --type=c -A3 -B3

Repository: nanomq/NanoNNG

Length of output: 1099


🏁 Script executed:

# Look for functions that might perform topic filtering during message forwarding
rg -n "topic.*match|match.*topic|filter.*topic" --type=c -l | head -10

Repository: nanomq/NanoNNG

Length of output: 278


🏁 Script executed:

# Find bridge-related source files
fd -e c "bridge" src/ | head -20

Repository: nanomq/NanoNNG

Length of output: 40


🏁 Script executed:

# Look for functions that process bridge messages/forwarding
rg -n "bridge.*forward|forward.*bridge|bridge.*message" --type=c -l | head -10

Repository: nanomq/NanoNNG

Length of output: 166


🏁 Script executed:

# Search for any function that checks or matches exclusions
rg -n "exclusion" --type=c -B5 -A5 | head -100

Repository: nanomq/NanoNNG

Length of output: 6778


Exclusions are parsed but never enforced at runtime.

The exclusions_list is populated during configuration parsing and freed during destruction, but there's no code that actually uses these exclusions to filter messages during bridge forwarding. This means the feature is incomplete—users can configure exclusions, but they won't have any effect.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/nng/supplemental/nanolib/conf.h` around lines 225 - 228, The
exclusions parsed into the exclusions struct (exclusions { char *topic; size_t
topic_len; }) and stored in exclusions_list are never consulted during bridge
forwarding; update the bridge message-forwarding path (where messages are
forwarded across the bridge) to iterate the exclusions_list and skip forwarding
when a message's topic matches any exclusions[i].topic using topic_len-aware
comparison (memcmp or strncmp) or exact string compare as appropriate; ensure
you use the same lifetime/ownership semantics as the parser (don't free entries
when skipping) and add unit/inline checks so that the forwarding function
returns early when a match is found.

Comment on lines +1267 to +1282
cJSON *exclusions_list = hocon_get_obj("exclusions", obj);
cJSON *exclusion = NULL;
cJSON_ArrayForEach(exclusion, exclusions_list)
{
exclusions *exc = NNI_ALLOC_STRUCT(exc);
hocon_read_str(exc, topic, exclusion);
size_t topic_len = exc->topic ? strlen(exc->topic) : 0;
if (topic_len == 0) {
log_error("Exclusion topic should not be null or empty");
NNI_FREE_STRUCT(exc);
break;
}
exc->topic_len = topic_len;
cvector_push_back(node->exclusions_list, exc);
}
node->exclusions_count = cvector_size(node->exclusions_list);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Using break on validation failure skips all remaining exclusions.

When an exclusion has an empty or null topic, the code breaks out of the loop entirely, discarding any subsequent valid exclusions. Consider using continue instead to skip only the invalid entry while still processing the rest.

Additionally, unlike the similar forwards/subscription parsing, there's no maximum topic length validation here. Extremely long topic strings could cause issues.

🐛 Proposed fix
 	cJSON *exclusions_list = hocon_get_obj("exclusions", obj);
 	cJSON *exclusion = NULL;
 	cJSON_ArrayForEach(exclusion, exclusions_list)
 	{
 		exclusions *exc = NNI_ALLOC_STRUCT(exc);
 		hocon_read_str(exc, topic, exclusion);
 		size_t topic_len = exc->topic ? strlen(exc->topic) : 0;
-		if (topic_len == 0) {
-			log_error("Exclusion topic should not be null or empty");
-			NNI_FREE_STRUCT(exc); 
+		if (topic_len == 0 || topic_len > UINT16_MAX) {
+			log_warn("Invalid exclusion topic length: %zu, skipping", topic_len);
+			if (exc->topic) {
+				nng_strfree(exc->topic);
+			}
+			NNI_FREE_STRUCT(exc);
-			break;
+			continue;
 		}
 		exc->topic_len = topic_len;
 		cvector_push_back(node->exclusions_list, exc);
 	}
 	node->exclusions_count = cvector_size(node->exclusions_list);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cJSON *exclusions_list = hocon_get_obj("exclusions", obj);
cJSON *exclusion = NULL;
cJSON_ArrayForEach(exclusion, exclusions_list)
{
exclusions *exc = NNI_ALLOC_STRUCT(exc);
hocon_read_str(exc, topic, exclusion);
size_t topic_len = exc->topic ? strlen(exc->topic) : 0;
if (topic_len == 0) {
log_error("Exclusion topic should not be null or empty");
NNI_FREE_STRUCT(exc);
break;
}
exc->topic_len = topic_len;
cvector_push_back(node->exclusions_list, exc);
}
node->exclusions_count = cvector_size(node->exclusions_list);
cJSON *exclusions_list = hocon_get_obj("exclusions", obj);
cJSON *exclusion = NULL;
cJSON_ArrayForEach(exclusion, exclusions_list)
{
exclusions *exc = NNI_ALLOC_STRUCT(exc);
hocon_read_str(exc, topic, exclusion);
size_t topic_len = exc->topic ? strlen(exc->topic) : 0;
if (topic_len == 0 || topic_len > UINT16_MAX) {
log_warn("Invalid exclusion topic length: %zu, skipping", topic_len);
if (exc->topic) {
nng_strfree(exc->topic);
}
NNI_FREE_STRUCT(exc);
continue;
}
exc->topic_len = topic_len;
cvector_push_back(node->exclusions_list, exc);
}
node->exclusions_count = cvector_size(node->exclusions_list);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/supplemental/nanolib/conf_ver2.c` around lines 1267 - 1282, The loop over
exclusions_list currently breaks on an invalid empty/null topic which aborts
processing remaining exclusions; change the behavior in the
cJSON_ArrayForEach(exclusion, exclusions_list) loop so that after
hocon_read_str(exc, topic, exclusion) you validate topic_len and on invalid
topic call log_error, free the allocated struct (NNI_FREE_STRUCT(exc)) and
continue (not break) to skip only that entry; additionally add the same
maximum-topic-length check used in forwards/subscription parsing (compare
topic_len against the established MAX_TOPIC_LEN constant or the same validation
routine), free exc and continue if it exceeds the max, otherwise set
exc->topic_len and cvector_push_back(node->exclusions_list, exc); finally ensure
node->exclusions_count = cvector_size(node->exclusions_list) remains after the
loop.

Comment on lines +3770 to +3776
log_info("%sbridge.mqtt.%s.exclusions: ", prefix, node->name);
for (size_t j = 0; j < node->exclusions_count; j++) {
log_info(
"\t[%ld] topic:\t\t%.*s", j,
node->exclusions_list[j]->topic_len,
node->exclusions_list[j]->topic);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential format string issue: %.*s precision expects int, but topic_len is size_t.

The %.*s format specifier expects an int for the precision argument, but topic_len is size_t. On 64-bit platforms where size_t is 64-bit, this could cause undefined behavior or incorrect output. The same issue exists for the loop index j being size_t but used with %ld.

🛡️ Proposed fix to cast the precision argument
 		log_info("%sbridge.mqtt.%s.exclusions: ", prefix, node->name);
 		for (size_t j = 0; j < node->exclusions_count; j++) {
 			log_info(
-				"\t[%ld] topic:\t\t%.*s", j, 
-				node->exclusions_list[j]->topic_len,
+				"\t[%zu] topic:\t\t%.*s", j, 
+				(int)node->exclusions_list[j]->topic_len,
 				node->exclusions_list[j]->topic);
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/supplemental/nanolib/conf.c` around lines 3770 - 3776, The format string
is unsafe: %.*s expects an int precision and %ld expects a long, but topic_len
is size_t and j is size_t; update the logging call in the loop that uses
node->exclusions_count, node->exclusions_list and
node->exclusions_list[j]->topic_len so the types match the format
specifiers—cast topic_len to int for the precision argument (e.g.,
(int)node->exclusions_list[j]->topic_len) and either switch %ld to %zu for j or
cast j to long (prefer %zu to match size_t); adjust the log_info invocation
accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant