Skip to content

Conversation

@gnodet
Copy link
Contributor

@gnodet gnodet commented Jan 27, 2026

CAMEL-22849: Fix AS2 server wildcard pattern matching for requestUriPattern

Summary

This PR fixes a regression in the camel-as2 component where AS2 server routes configured with wildcard patterns in requestUriPattern (e.g., /receiver/*) were not correctly resolving the appropriate AS2ConsumerConfiguration for incoming requests.

Problem

In Camel 4.14.x, when an AS2 request was sent to a concrete path like /receiver/test-1, the server would fail to find the consumer configuration for patterns like /receiver/*, logging:

No AS2 consumer configuration found for canonical path: /receiver/test-1.
Encrypted messages will likely fail.

The root cause was that AS2ServerConnection.getConfigurationForPath(String path) performed only an exact map lookup using the resolved request path, which would never match wildcard patterns stored as keys like /receiver/*.

This broke encrypted and/or signed AS2 messages because the server could not locate the appropriate consumer configuration for decryption and signature validation. This was a regression from Camel 3.22.x where wildcard patterns worked correctly.

Solution

Updated AS2ServerConnection to support wildcard pattern matching:

  1. Pattern Matching Logic: Modified getConfigurationForPath() to:

    • First attempt an exact match (for performance)
    • If no exact match, iterate through registered patterns in insertion order
    • Return the first matching pattern (based on route definition order)
  2. First-Match Ordering: Uses LinkedHashMap (wrapped in Collections.synchronizedMap()) to preserve insertion order:

    • When multiple patterns match a request, the first registered pattern wins
    • Developers control matching priority through route definition order
    • More specific patterns should be defined before more general patterns
  3. Regex-based Wildcard Support: Implemented matchesPattern() method that:

    • Uses compiled regex patterns with caching for performance
    • Properly escapes all regex special characters using Pattern.quote() to ensure only * has special meaning
    • Supports wildcards in any position (e.g., /api/*/orders, /receiver/*, /*)
  4. Pattern Caching: Added a compiledPatterns cache to avoid recompiling regex patterns on every request

  5. Comprehensive Tests: Added AS2ServerWildcardPatternIT with extensive test coverage for:

    • Basic wildcard matching (/consumer/*)
    • Exact match precedence over wildcards
    • First-match ordering with multiple overlapping patterns
    • Nested wildcards (/consumer/orders/*)
    • Multiple wildcards in one pattern (/api/*/orders/*)
    • Edge cases (root wildcard /*, special characters in paths)

⚠️ Important: Pattern Matching Behavior

First-Match vs Longest-Match

This implementation uses first-match ordering (based on route definition order) rather than longest-match ordering. This decision was made because:

  1. More Deterministic: LinkedHashMap preserves insertion order, eliminating ambiguity when patterns have equal specificity
  2. Developer Control: Route definition order gives developers explicit control over matching priority
  3. Simpler Logic: No need for complex tie-breaking rules
  4. Standard Routing Behavior: Matches how most routing frameworks work (first matching route wins)

Breaking Change Consideration

Is this a breaking change? Technically yes, but the impact is negligible because:

  1. The wildcard feature was broken in Camel 4.14.x - users couldn't have been relying on behavior that didn't work
  2. This is a bug fix, not a new feature - we're restoring functionality from Camel 3.22.x
  3. Most users use exact matches - AS2 endpoints typically use specific paths, not wildcards
  4. Single pattern per path - Most deployments likely have only one pattern that would match any given request

Best Practice: When defining routes with overlapping wildcard patterns, define more specific patterns before more general ones:

// ✅ CORRECT: More specific patterns first
from("as2://server/listen?requestUriPattern=/consumer/orders")      // Exact match
    .to("mock:exactConsumer");

from("as2://server/listen?requestUriPattern=/consumer/orders/*")   // Specific wildcard
    .to("mock:specificConsumer");

from("as2://server/listen?requestUriPattern=/consumer/*")          // General wildcard
    .to("mock:generalConsumer");

// ❌ INCORRECT: General pattern first would match everything
from("as2://server/listen?requestUriPattern=/consumer/*")          // Would match all /consumer/* requests
    .to("mock:generalConsumer");

from("as2://server/listen?requestUriPattern=/consumer/orders/*")   // Would never be reached
    .to("mock:specificConsumer");

Important Remarks

1. Risk of Ambiguity ✅ RESOLVED

Original Issue: When two patterns had the same "specificity length", the matching order was not guaranteed due to ConcurrentHashMap iteration order.

Solution Implemented: Changed to LinkedHashMap with first-match ordering. The first registered pattern (in route definition order) now takes precedence, making behavior 100% deterministic.

Developer Responsibility: Define more specific patterns before more general patterns in route configuration.

2. Missing Edge Case Test

Issue: The current tests verify wildcard * matching, but do not verify what happens if the pattern contains other regex special characters (like . or +).

Current Protection: The implementation uses Pattern.quote() to properly escape all regex special characters in each segment between wildcards, ensuring that only * is interpreted as a wildcard.

Recommendation: Add a test case to explicitly verify that patterns containing regex special characters (e.g., /receiver/test.endpoint, /api/v1+2/orders) are treated as literal characters and not as regex operators. This would ensure Pattern.quote() is working correctly and prevent future regressions.

3. Security Consideration (Minor)

Issue: There is a theoretical risk of "ReDoS" (Regular Expression Denial of Service) if a malicious user configures an extremely complex pattern.

Current Risk Assessment: The risk is low in this implementation because:

  • Patterns are configured by administrators, not end users
  • The regex patterns are simple (only .* for wildcards)
  • Pattern compilation happens once and is cached

Best Practice: This is noted as a good practice to keep in mind for future enhancements. If pattern complexity increases or if patterns become user-configurable, additional validation or complexity limits should be considered.

Testing

  • All existing tests pass
  • New comprehensive integration test AS2ServerWildcardPatternIT covers:
    • Basic wildcard matching
    • Exact match precedence
    • First-match ordering with overlapping patterns
    • Multiple wildcards
    • Edge cases

Compatibility

This change restores the wildcard pattern matching behavior from Camel 3.22.x. Since the wildcard feature was broken in 4.14.x, this is considered a bug fix rather than a breaking change.

Fixes: https://issues.apache.org/jira/browse/CAMEL-22849


Pull Request opened by Augment Code with guidance from the PR author

The AS2 server/listen functionality was not properly resolving requestUriPattern wildcards when selecting the appropriate consumer configuration. The getConfigurationForPath method in AS2ServerConnection was doing a simple Map.get() lookup which only supported exact matches.

This fix adds wildcard pattern matching support to the getConfigurationForPath method:
- First tries exact match for performance
- Then tries pattern matching for wildcards (e.g., /consumer/*)
- Prefers longer (more specific) patterns when multiple patterns match
- Supports wildcard '*' which matches any sequence of characters

The RequestHandlerRegistry from HttpCore5 already supports wildcard patterns when registering handlers, but the configuration lookup was failing because it required exact matches. This fix ensures that both the handler and configuration lookups support the same wildcard patterns.
This test verifies that the AS2 server correctly handles wildcard patterns
in the requestUriPattern parameter. It tests that paths like /consumer/orders
and /consumer/invoices both match the pattern /consumer/*.

The test includes three scenarios:
1. Matching /consumer/orders against /consumer/*
2. Matching /consumer/invoices against /consumer/*
3. Matching nested paths like /consumer/orders/123 against /consumer/*

All tests verify that the AS2 server correctly routes messages to the
appropriate consumer based on wildcard pattern matching.
…uestUriPattern

This commit adds support for wildcard patterns in the AS2 server component's
requestUriPattern parameter. Previously, only exact path matching was supported,
which limited the flexibility of AS2 server endpoint configuration.

Changes:
- Modified AS2ServerConnection.findConsumer() to use PathMatcher for pattern matching
- Patterns are sorted by specificity: exact matches first, then wildcards by length
- Added comprehensive integration tests for wildcard pattern functionality

The implementation supports patterns like:
- /consumer/* - matches any path starting with /consumer/
- /consumer/orders/* - matches paths starting with /consumer/orders/
- /consumer/orders - exact match only

Exact matches take precedence over wildcard patterns, and more specific
patterns take precedence over less specific ones.
@github-actions
Copy link
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟

🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run

  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot.

  • You can label PRs using build-all, build-dependents, skip-tests and test-dependents to fine-tune the checks executed by this PR.

  • Build and test logs are available in the Summary page. Only Apache Camel committers have access to the summary.

  • ⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

Changed from longest-match to first-match ordering for wildcard pattern
resolution. When multiple patterns match a request, the first registered
pattern (in route definition order) now takes precedence.

This approach is more deterministic and gives developers full control
over pattern matching priority through route definition order. More
specific patterns should be defined before more general patterns.

Updated LinkedHashMap (wrapped in Collections.synchronizedMap()) to
preserve insertion order, replacing ConcurrentHashMap which had no
ordering guarantee.
@davsclaus
Copy link
Contributor

There are uncommitted changes
HEAD detached at pull/21089/merge
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git restore ..." to discard changes in working directory)
modified: components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java

@gnodet gnodet merged commit 741f754 into apache:main Jan 27, 2026
4 checks passed
davsclaus pushed a commit that referenced this pull request Jan 28, 2026
…attern (#21089)

The AS2 server/listen functionality was not properly resolving requestUriPattern wildcards when selecting the appropriate consumer configuration. The getConfigurationForPath method in AS2ServerConnection was doing a simple Map.get() lookup which only supported exact matches.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants