[SPR-9655] HttpHeaders.getAccept() does not return all accept headers for Iplanet WebServer 7 Created: 30/Jul/12  Updated: 22/Jul/16  Resolved: 22/Oct/12

Status: Closed
Project: Spring Framework
Component/s: Web
Affects Version/s: 3.0.7
Fix Version/s: 3.1.3, 3.2 RC1

Type: Improvement Priority: Major
Reporter: Marty Jones Assignee: Rossen Stoyanchev
Resolution: Complete Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Relate
is related to SPR-14506 HeaderContentNegotiationStrategy does... Closed
Days since last comment: 5 years, 48 weeks, 2 days ago
Last commented by a User: true
Last updater: Juergen Hoeller

 Description   

The HttpHeaders.getAccept() method is supposed to return a list of "accept" header MediaTypes that were on the servlet request. I noticed that only the first accept header was being returned when invoking this method. I debugged the code and found that getAccept() method invokes the getFirst() method and then splits the comma delimited string to get the list of MediaTypes. This works fine on tomcat because tomcat returns the request accept headers as a single comma separated string. However Oracle Iplanet Webserver returns each request accept header as a separate header entry so it will only ever return the first accept header.

I ran into this problem when I was invoking a controller that returned a json object. If I invoked the controller directly from the browser url I would get the string representation of the json object when running in a tomcat server. However when I ran the same code in a Iplanet WebServer I would get "org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation". I started digging into the code and that is where I found that the AnnotationMethodHandlerAdapter.writeWithMessageConverters() method is calling the inputMessage.getHeaders().getAccept() method which when run inside a tomcat server would return 4 accept headers, one of which was "/". When ran in Iplanet WebServer it only ever returned "text/html". This would cause the error since the MappingJacksonHttpMessageConverter only accepts "application/json".

Basically the HttpHeaders.getAccept() method needs to take into account that not all servlet containers return all the header values as a single comma separated string. You should be able to just check the length of the returned list of strings when getting the accept headers and if the length is 1 then parse it. It if has more than one entry then just concatenate the string list together as a comma separated string.



 Comments   
Comment by Marty Jones [ 30/Aug/12 ]

Has there been any progress on this issue? Would it help if I created a source patch that would fix this issue?

Comment by Rossen Stoyanchev [ 30/Aug/12 ]

What would help is to provide examples of the Accept header on the wire and how it's returned by the HttpServletRequest. Is this behavior specific to iPlanet WebServer version 7?

Comment by Rossen Stoyanchev [ 30/Aug/12 ]

Re-classifying from Bug to Improvement.

Comment by Marty Jones [ 30/Aug/12 ]

Rossen,

I disagree that this should be classified as an improvement. The servlet spec "http://docs.oracle.com/javaee/5/api/javax/servlet/http/HttpServletRequest.html#getHeaders%28java.lang.String%29" states that "Some headers, such as Accept-Language can be sent by clients as several headers each with a different value rather than sending the header as a comma separated list.".

That is what is happening in this case. Tomcat returns a single accept header as a comma separated string. Iplanet returns a separate accept header for each accept value passed over. I debugged the ServletServerHttpRequest.getHeaders() method and captured the results from line 95 of this class.

Here are the results:

tomcat -> The enumeration returns one value of "text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8".

iplanet -> The enumeration returns 4 values ["text/html","application/xhtml+xml","application/xml;q=0.9","*/*;q=0.8"]

This is causing us major issues because the HttpHeaders.getAccept() is only ever returning the "text/html" entry on iPlanet.

Comment by Rossen Stoyanchev [ 30/Aug/12 ]

I am not sure that interpretation is accurate. Here is a quote from the Servlet specification that is a little more clear than the Javadoc:

The getHeader method returns a header given the name of the header.
There can be multiple headers with the same name, e.g. Cache-Control
headers, in an HTTP request. If there are multiple headers with the
same name, the getHeader method returns the first header in the
request. The getHeaders method allows access to all the header values
associated with a particular header name, returning an Enumeration
of String objects.

The key cases are multiple headers with the same name or one header with comma-separated values. Even in the Javadoc it is phrased as an either-or proprosition: "several headers each with a different value rather than sending the header as a comma separated list."

This is why I asked for what you see on the wire (with Firebug, Chrome dev tools, etc). I suspect it is a single header with a comma-separated list of media types that iPlanet is parsing. If that is the case we will consider it an improvement, or something extra we do to make sure it works on iPlanet.

Comment by Marty Jones [ 30/Aug/12 ]

Sorry, I misunderstood what you were asking for. I used firebug to look at the headers and sure enough there is only use accept header.

The value is "text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8".

Looks like Iplanet is parsing the header into multiple entries on our behalf (why, oh why do they do that?). The funny thing is that I believe Iplanet is running tomcat under the covers.

Just another reason why I want to move away from Iplanet.

Do you know of any work around to be able to get all the header values?

Comment by Rossen Stoyanchev [ 30/Aug/12 ]

Ok, good to know.

Well, you could create a Filter that wraps the request and returns the Accept header value as a comma-separated String. As long as that doesn't impact any other code that relies on the iPlanet behavior, that should work. Another other option is to patch HttpHeaders temporarily (until this issue is resolved), for example by copying it to your source directory, preserving the package name, and modify it.

Comment by Marty Jones [ 18/Sep/12 ]

Here is a patch I made that resolves the issue:

public List<MediaType> getAccept() {

    String value = null;
    List<String> headerValues = headers.get(ACCEPT);

    if (headerValues != null && headerValues.size() > 0) {
        if (headerValues.size() == 1) {
            value = headerValues.get(0);
        }
        else if (headerValues.size() > 1) {
            value = StringUtils.collectionToCommaDelimitedString(headerValues);
        }
    }

    return (value != null ? MediaType.parseMediaTypes(value) : Collections.<MediaType>emptyList());
}
Generated at Thu Aug 16 14:11:14 UTC 2018 using JIRA 7.9.2#79002-sha1:3bb15b68ecd99a30eb364c4c1a393359bcad6278.