Add wildcard forced hosts (fix #1587)#1826
Conversation
| .filter(entry -> AddressUtil.isHostMatchingPattern(entry.getKey(), virtualHostStr)) | ||
| .map(Map.Entry::getValue) | ||
| .findFirst() | ||
| .orElse(Collections.emptyList()) |
There was a problem hiding this comment.
Three nits:
1 - This is a pretty large method chain that's duplicated twice, it would probably make sense to extract this out into its own method.
2 - the "or default" value is always computed, even in the case where the exact virtualHostStr mapping is present. And the .orElse(Collections.emptyList()) also always returns an empty list. Fortunately this doesn't comput or allocate anything, but this means that all three cases - exact match, pattern match, empty list - are always computed, even when an exact match could abort the latter two, which is a smell
3 - Essentially this is a giant double if-else statement:
if (/* exact forced host defined */) {
serversToTry = /* exact forced host */;
} else if (/* isHostMatchingPattern matches */) {
serversToTry = /* isHostMatchingPattern result */;
} else {
serversToTry = Collections.emptyList();
}However this is disguised in getOrDefault (first two if and if else) and .orElse(Collections.emptyList() (last else). I would probably make this more explicit, either something similar to the above pseudo code or using an optional chain with 2x .orElseGet(), keeping it all in optional-land.
There was a problem hiding this comment.
Good call, I was thinking about this too. Where do you think the right place to hold this new method could be? I would have put it in ConnectedPlayer, but this same pattern is in ServerListPingHandler as well, which are two separate things from each other. Thoughts?
There was a problem hiding this comment.
AddressUtil seems like the right place, as you have already introduced the new isHostMatchingPattern there.
Consider making it return an Optional<String>, allowing the caller to change an empty result to an empty list (ConnectedPlayer) or server.getConfiguration().getAttemptConnectionOrder() (ServerListPingHandler). (with orElseGet() to avoid computing when the optional is present)
There was a problem hiding this comment.
See 54d6816. I've implemented it using Optional<String> but not 2x .orElseGet() since I find using if/else and a for loop a bit more readable, but let me know if you think otherwise
…ngPattern statically, import JUnit methods statically to match other tests
I added wildcard support to
[forced-hosts], resolving #1587. Unit tests pass and checkstyle passes and I've tested this against my own running proxy. I'd appreciate feedback or additional testing if needed, especially from those on #1587 who submitted the issue.What this does
Consider the example from #1587
Now this can be changed to:
How matching works
I added
AddressUtil.isHostMatchingPattern(pattern, host). Patterns are matched on each label where*matches exactly one label. To be matched, patterns must have the same number of labels as the host. Matching is case insensitive. For example:*.example.commatchesa.example.combut notexample.com(fewer labels) orb.a.example.com(more labels).b.*.example.commatchesb.a.example.comMatching order
In both
ConnectedPlayerandServerListPingHandler, forced-host lookup now follows a hierarchy where exact matches always win over wildcards, so adding a*.example.comentry won't change routing for any host you've already defined explicitly.getAttemptConnectionOrder()inServerListPingHandler, or an empty collection inConnectedPlayer. this are both unchanged from what was before).Tests
I added
ForcedHostsTestcoveringisHostMatchingPatterncases for single and mid-pattern wildcards, label-count mismatches, and a non-matching domain.Potential issue
If two different wildcard patterns could match the same host, the one chosen depends on config map iteration order. Would like feedback on what the behavior should be here