Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.Sign up
Consistent handling of multi-valued headers in HttpHeaders [SPR-14223] #18797
The org.springframework.http.HttpHeaders class wraps HTTP request headers and makes them easy to work with (e.g., parsing HTTP dates such as in the Last-Modified header) for you.
However, it treats multi-valued headers incorrectly in my opinion. From RFC2616 https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2:
Thus, it seems to me that the following sets of headers mean the same thing:
In all cases the header value of the X-List header is the list of strings "a", "b", "c". Note that a proxy may convert the second or third form to the first form.
According to the documentation on HttpHeaders:
I would expect in all 3 cases that getFirst("X-List") returns the string "a". However this is not the case. In the first case it returns "a, b, c", in the second case "a", and in the third case "a, b".
Note that this is actually a problem in the handling of some of the headers (the various Access-Control headers, the If-None-Match header), because they call getFirst or getFirstValueAsList. But this way they ignore any values in repeated headers, which are valid and should be appended to the list according to the RFC.
Since the processing of multiple header values is rather complicated, it would be really helpful if the HttpHeaders class removed this complexity from the API user.
As commented in #18790, there is a problem with the method to split the header into values (getFirstValueAsList): the values themselves may contain commas (ETags are an example of this). It seems that to split the header you need to understand the syntax of the values themselves. I don't see how one could implement this correctly for the generic case (e.g., unknown X-* headers). However, for the known HTTP headers it should be possible to split the list correctly.
Referenced from: commits 55dae61
Brian Clozel commented
Just a note on
I can see why the
Sebastiaan van Erk commented
I think I misunderstood where "getFirst" came from: I just noticed it's inherited from the MultiValueMap, and as such, in all cases where the header is a "non-list" header you obviously want to return the whole header (and not split it on commas). So I agree it's a bad idea to change the way "getFirst" works. I thought it was the intention of "getFirst" to return the first element of a list header, in which case the above behavior would be weird. I guess it's easy to be confused due to the name of the method (in the case of a "non-list" header, "getFirst" sounds weird because there is only one).
Anyway, for "list" headers I don't see why I would ever care how the list is communicated to me: as 1 header or as multiple, or how it was communicated to me as multiple (e.g., "a,b" and "c" or "a" and "b,c"). As an API user I don't want to deal with the complexity of treating those as different cases. For me it's just a list of values. And in this case, I don't really see a use case for a "getFirstListValue" method (which is how I interpreted the existing "getFirst" method). However, I would very much like to call "getValuesAsList" (so the complexity of merging headers and splitting values has already been done for me). It would be very useful if that method was public.
Brian Clozel commented
This is now fixed with 55dae618a6.
Note that the new
This will be available in the next SNAPSHOT version. Feel free to voice your opinion before the next RC version next week!