fix: accept header accessor function in ServerTransportSecurityValidator#982
Conversation
Replace Map<String, List<String>> parameter with Function<String, List<String>> in validateHeaders(), allowing callers to pass a header accessor instead of extracting all headers upfront. This is more efficient (only requested headers are looked up) and delegates case-insensitive header matching to the underlying request implementation (e.g. HttpServletRequest.getHeaders). - Update DefaultServerTransportSecurityValidator to use the accessor directly for Origin and Host headers - Update all three servlet transport providers to pass name -> Collections.list(request.getHeaders(name)) - Remove HttpServletRequestUtils (no longer needed) - Update unit tests to use accessor-based API Closes modelcontextprotocol#870
|
The proposed changed would be breaking. The original |
Instead of a breaking change, introduce a deprecation path: - Keep validateHeaders(Map) as deprecated default method that bridges to the new validateHeaders(Function) via case-insensitive lookup - New implementations override validateHeaders(Function) for efficiency - Transport providers continue calling validateHeaders(Map) for backward compatibility with existing custom implementations - Restore HttpServletRequestUtils for header extraction in transports - Add tests for deprecated Map-based API and interface bridge behavior
|
Updated with a deprecation path as suggested. The changes:
|
|
@Kehrlann I've updated the PR with the deprecation path as you suggested. Would you mind taking another look when you get a chance? Thanks! |
|
Hey @neerajbhatt ! Thanks for the update. So this adds a deprecation path but the transport does not use the new API. But that's the whole point in introducing this new API. Let's try introducing a
And then @FunctionalInterface
public interface ServerTransportSecurityValidator {
@Deprecated
void validateHeaders(Map<String, List<String>> headers) throws ServerTransportSecurityException;
default void validateHeaders(HeaderAccessor accessor) throws ServerTransportSecurityException {
var collectedHeaders = accessor.getHeaderNames()
.stream()
.collect(Collectors.<String, String, List<String>>toUnmodifiableMap(String::toLowerCase,
accessor::getHeader, (l1, l2) -> {
var merged = new ArrayList<>(l1);
merged.addAll(l2);
return Collections.unmodifiableList(merged);
}));
validateHeaders(collectedHeaders);
}
} |
Replace Function<String, List<String>> with a dedicated HeaderAccessor interface (getHeader + getHeaderNames) as suggested in review. Transports now pass an HttpServletHeaderAccessor wrapping the request directly, leveraging the servlet's native case-insensitive header lookup instead of extracting all headers into a Map upfront.
b16ccde to
cee7a22
Compare
|
@Kehrlann Updated with the New files:
Modified files:
Removed:
Tests:
|
Summary
HeaderAccessorinterface withgetHeader(String)andgetHeaderNames()methods, replacing the rawFunction<String, List<String>>parameter inServerTransportSecurityValidator.validateHeaders(), as proposed inServerTransportSecurityValidatoraccepts an accessor function for header #870 and per reviewer feedbackHttpServletHeaderAccessorwrapping the request directly, leveraging the servlet's native case-insensitive header lookup instead of extracting all headers into aMapupfrontvalidateHeaders(Map)is preserved as a default method that bridges to the newvalidateHeaders(HeaderAccessor), ensuring backward compatibility for existing custom implementationsHttpServletRequestUtilsutility class (no longer needed)Changes
HeaderAccessor— new public interface withgetHeader(String)andgetHeaderNames()HttpServletHeaderAccessor— internal implementation wrappingHttpServletRequestServerTransportSecurityValidator— newvalidateHeaders(HeaderAccessor)method; deprecatedvalidateHeaders(Map)default bridges to it; both defaults cross-delegate so existing impls work either wayDefaultServerTransportSecurityValidator— overridesvalidateHeaders(HeaderAccessor), queriesOriginandHostvia accessor directlynew HttpServletHeaderAccessor(request)instead ofextractHeaders(request)HeaderAccessor; addedaccessorDefaultBridgesToMapOverridetest for the reverse bridge directionTest plan
DefaultServerTransportSecurityValidatorTestspassCloses #870