Class CaseInsensitiveHeaderMap
LinkedHashMap of HTTP header name -> values whose lookups (get,
containsKey, getOrDefault) are case-insensitive on the header name, while
iteration order and the original key casing are preserved exactly as inserted.
HTTP field names are case-insensitive (RFC 9110 §5.1) and MockServer's matchers treat them
so, but response templates read request headers through plain Map semantics — Velocity's
Uberspector maps $request.headers.host to get("host") and Mustache's fetcher calls
containsKey then get. With a plain LinkedHashMap a template that asked for
$request.headers.Host could therefore miss a header the client sent as host (or
vice-versa), and the mismatch was protocol-dependent because HTTP/2 lowercases field names on the
wire while HTTP/1.1 preserves the client's casing (issue #2575).
This map is deliberately additive, not normalising:
- An exact-case hit is returned unchanged (fast path); only a miss falls back to a
case-insensitive scan. So every casing combination resolves —
Host→host,host→Host, and the two same-case combinations that already worked — and keys are never lowercased, so$request.headers.Hostkeeps working when the client sentHost. - Extending
LinkedHashMapkeeps iteration (viakeySet/values/entrySet, used both by loop-over-headers templates and by Jackson serialisation) in the original insertion order with the original wire casing — unlike aTreeMap<>(String.CASE_INSENSITIVE_ORDER), which would reorder alphabetically. - If two differently-cased spellings of the same name arrive as separate fields (e.g.
Hostandhost),put(java.lang.String, java.util.List<java.lang.String>)merges their values into the single first-seen entry rather than one silently overwriting the other, matching HTTP's rule that same-name fields combine. The first-seen casing is kept as the canonical key.
The case-insensitive resolution is a scan of the live entries rather than a maintained
lower-case index, deliberately: HashMap routes its bulk and default methods
(putAll, putIfAbsent, merge, compute*) straight at its internal
storage without ever calling the overridden put(java.lang.String, java.util.List<java.lang.String>), and the keySet/entrySet
views mutate it directly too, so any side index silently goes stale — and this map is public and
reachable from template expressions, where Velocity's Uberspector will invoke exactly those public
methods. Deriving from the entries instead cannot go stale by construction. Header counts are
small (tens of entries) and the scan runs only on a miss, so the cost is immaterial.
Only the response/forward templates package uses this. It is intentionally NOT the codebase's
general header type: org.mockserver.model.Headers is a
Multimap<NottableString, NottableString> that serialises as a DTO array and offers no
Map.get semantics, so it cannot be consumed by the template engines here.
Header name lookup only — this type is not used for cookies, query-string parameters or path parameters, whose names are case-sensitive per their respective specifications.
- See Also:
-
Nested Class Summary
Nested classes/interfaces inherited from class java.util.AbstractMap
AbstractMap.SimpleEntry<K extends Object,V extends Object>, AbstractMap.SimpleImmutableEntry<K extends Object, V extends Object> -
Constructor Summary
Constructors -
Method Summary
Methods inherited from class java.util.LinkedHashMap
clear, containsValue, entrySet, forEach, keySet, removeEldestEntry, replaceAll, valuesMethods inherited from class java.util.HashMap
clone, compute, computeIfAbsent, computeIfPresent, isEmpty, merge, putAll, putIfAbsent, remove, replace, replace, sizeMethods inherited from class java.util.AbstractMap
equals, hashCode, toStringMethods inherited from class java.lang.Object
finalize, getClass, notify, notifyAll, wait, wait, waitMethods inherited from interface java.util.Map
compute, computeIfAbsent, computeIfPresent, equals, hashCode, isEmpty, merge, putAll, putIfAbsent, remove, replace, replace, size
-
Constructor Details
-
CaseInsensitiveHeaderMap
public CaseInsensitiveHeaderMap()
-
-
Method Details
-
put
-
get
-
containsKey
- Specified by:
containsKeyin interfaceMap<String,List<String>> - Overrides:
containsKeyin classHashMap<String,List<String>>
-
getOrDefault
- Specified by:
getOrDefaultin interfaceMap<String,List<String>> - Overrides:
getOrDefaultin classLinkedHashMap<String,List<String>>
-
remove
-