Skip to content

Feature/url redirect filter#95

Open
Ericthilen wants to merge 78 commits intomainfrom
feature/url-redirect-filter
Open

Feature/url redirect filter#95
Ericthilen wants to merge 78 commits intomainfrom
feature/url-redirect-filter

Conversation

@Ericthilen
Copy link

@Ericthilen Ericthilen commented Feb 25, 2026

Summary by CodeRabbit

  • New Features

    • Adds HTTP redirect handling with wildcard and regex path matching and a source-path pattern compiler.
    • Supports 301 (permanent) and 302 (temporary) redirects; invalid status codes are rejected.
    • Request paths are sanitized before redirecting; non-matching requests continue through normal processing.
  • Tests

    • Added tests for redirect behavior, pattern matching, sanitization, and status-code validation.

Ericthilen and others added 30 commits February 11, 2026 17:34
feat: add regex support and enforce 301/302 redirects
@Ericthilen Ericthilen force-pushed the feature/url-redirect-filter branch from 900dcc3 to 3bd0419 Compare February 25, 2026 13:40
Copy link

@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: 1

🧹 Nitpick comments (2)
src/main/java/org/example/filter/redirect/RedirectRulesLoader.java (2)

26-31: Document that * matches only within a single path segment.

[^/]* excludes the / separator, so /docs/* matches /docs/foo but not /docs/foo/bar. Users expecting glob-style ** will get silent non-matching behaviour. A brief Javadoc note on compileSourcePattern or wildcardToRegex clarifying this constraint prevents confusion.

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

In `@src/main/java/org/example/filter/redirect/RedirectRulesLoader.java` around
lines 26 - 31, Add a brief Javadoc note to the pattern compilation helper (e.g.,
compileSourcePattern and/or wildcardToRegex) stating that the single asterisk
'*' is translated to a segment-local pattern that excludes the path separator
(i.e., uses [^/]*), so '/docs/*' matches '/docs/foo' but not '/docs/foo/bar',
and that glob-style recursive matching ('**') is not supported by this
implementation; document this constraint and behavior so callers won't be
surprised by silent non-matches.

34-45: Accumulate literal runs before quoting to produce cleaner patterns.

Calling Pattern.quote(String.valueOf(c)) for every non-* character generates a \Qx\E wrapper around each individual character (e.g., /docs/*\Q/\E\Qd\E\Qo\E\Qc\E\Qs\E\Q/\E[^/]*). Accumulating consecutive literals into a single buffer and quoting the whole segment is equivalent and much more readable in logs/debuggers.

♻️ Proposed refactor
     private static String wildcardToRegex(String wildcard) {
         StringBuilder sb = new StringBuilder();
+        StringBuilder literal = new StringBuilder();
         for (int i = 0; i < wildcard.length(); i++) {
             char c = wildcard.charAt(i);
             if (c == '*') {
-                sb.append("[^/]*");
+                if (!literal.isEmpty()) {
+                    sb.append(Pattern.quote(literal.toString()));
+                    literal.setLength(0);
+                }
+                sb.append("[^/]*");
             } else {
-                sb.append(Pattern.quote(String.valueOf(c)));
+                literal.append(c);
             }
         }
+        if (!literal.isEmpty()) {
+            sb.append(Pattern.quote(literal.toString()));
+        }
         return sb.toString();
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/filter/redirect/RedirectRulesLoader.java` around
lines 34 - 45, The wildcardToRegex method currently quotes each non-asterisk
character individually which produces many \Q...\E fragments; modify
wildcardToRegex in RedirectRulesLoader to accumulate consecutive literal
characters in a temporary StringBuilder and only call Pattern.quote once per
run: iterate over wildcard, when you see a '*' flush the literal buffer (if
non-empty) by appending Pattern.quote(buffer.toString()) to the main sb and
clear it, then append the existing "[^/]*" for '*'; after the loop flush any
remaining literal buffer similarly so the final regex has consolidated quoted
runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/filter/redirect/RedirectRule.java`:
- Around line 17-19: The targetUrl validation in RedirectRule only checks for
CR/LF but misses null-byte characters which can also terminate headers; update
the validation in the RedirectRule constructor (or where targetUrl is checked)
to also detect '\0' (null character) — e.g., check targetUrl for '\r', '\n' or
'\0' (or use contains("\0")/indexOf('\u0000') >= 0) and throw the same
IllegalArgumentException("targetUrl must not contain CR/LF") (adjust message to
mention null byte if desired) when found.

---

Nitpick comments:
In `@src/main/java/org/example/filter/redirect/RedirectRulesLoader.java`:
- Around line 26-31: Add a brief Javadoc note to the pattern compilation helper
(e.g., compileSourcePattern and/or wildcardToRegex) stating that the single
asterisk '*' is translated to a segment-local pattern that excludes the path
separator (i.e., uses [^/]*), so '/docs/*' matches '/docs/foo' but not
'/docs/foo/bar', and that glob-style recursive matching ('**') is not supported
by this implementation; document this constraint and behavior so callers won't
be surprised by silent non-matches.
- Around line 34-45: The wildcardToRegex method currently quotes each
non-asterisk character individually which produces many \Q...\E fragments;
modify wildcardToRegex in RedirectRulesLoader to accumulate consecutive literal
characters in a temporary StringBuilder and only call Pattern.quote once per
run: iterate over wildcard, when you see a '*' flush the literal buffer (if
non-empty) by appending Pattern.quote(buffer.toString()) to the main sb and
clear it, then append the existing "[^/]*" for '*'; after the loop flush any
remaining literal buffer similarly so the final regex has consolidated quoted
runs.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a004c75 and 3bd0419.

📒 Files selected for processing (6)
  • src/main/java/org/example/filter/redirect/RedirectFilter.java
  • src/main/java/org/example/filter/redirect/RedirectRule.java
  • src/main/java/org/example/filter/redirect/RedirectRulesLoader.java
  • src/main/resources/.gitkeep
  • src/test/java/org/example/filter/redirect/RedirectFilterTest.java
  • src/test/resources/.gitkeep
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/test/java/org/example/filter/redirect/RedirectFilterTest.java
  • src/main/java/org/example/filter/redirect/RedirectFilter.java

Copy link

@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: 1

🧹 Nitpick comments (1)
src/main/java/org/example/filter/redirect/RedirectRule.java (1)

36-43: Consider implementing equals and hashCode.

RedirectRule is a value object with three final fields. Without overriding equals/hashCode, two logically identical rules won't be considered equal in Set, Map, or contains checks, which can cause silent deduplication failures if the rule list is ever placed in a collection.

♻️ Proposed addition
+    `@Override`
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof RedirectRule other)) return false;
+        return statusCode == other.statusCode
+                && targetUrl.equals(other.targetUrl)
+                && sourcePattern.pattern().equals(other.sourcePattern.pattern())
+                && sourcePattern.flags() == other.sourcePattern.flags();
+    }
+
+    `@Override`
+    public int hashCode() {
+        return Objects.hash(sourcePattern.pattern(), sourcePattern.flags(), targetUrl, statusCode);
+    }
+
     `@Override`
     public String toString() {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/filter/redirect/RedirectRule.java` around lines 36
- 43, Add proper value semantics to RedirectRule by implementing equals(Object)
and hashCode() that consider the three final fields: sourcePattern, targetUrl,
and statusCode. In equals, check identity, class, then compare sourcePattern and
targetUrl with Objects.equals and compare primitive statusCode directly; in
hashCode return Objects.hash(sourcePattern, targetUrl, statusCode). Ensure both
methods are declared in the RedirectRule class alongside the existing toString
so instances behave correctly in Sets/Maps and contains checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/filter/redirect/RedirectRule.java`:
- Around line 22-24: The validation in RedirectRule currently rejects 307 and
308; update the check that inspects statusCode in class RedirectRule (the block
that throws IllegalArgumentException) to allow HTTP 301, 302, 307 and 308 (i.e.,
accept 301 || 302 || 307 || 308) and adjust the IllegalArgumentException message
to list the allowed codes (301, 302, 307, 308) so callers get accurate guidance;
ensure any surrounding logic that assumes only 301/302 still works with 307/308
(preserve method semantics).

---

Nitpick comments:
In `@src/main/java/org/example/filter/redirect/RedirectRule.java`:
- Around line 36-43: Add proper value semantics to RedirectRule by implementing
equals(Object) and hashCode() that consider the three final fields:
sourcePattern, targetUrl, and statusCode. In equals, check identity, class, then
compare sourcePattern and targetUrl with Objects.equals and compare primitive
statusCode directly; in hashCode return Objects.hash(sourcePattern, targetUrl,
statusCode). Ensure both methods are declared in the RedirectRule class
alongside the existing toString so instances behave correctly in Sets/Maps and
contains checks.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3bd0419 and f63637b.

📒 Files selected for processing (1)
  • src/main/java/org/example/filter/redirect/RedirectRule.java

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.

2 participants