Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HttpHeaders.getAccept() does not return all accept headers for Iplanet WebServer 7 [SPR-9655] #14289

Closed
spring-issuemaster opened this Issue Jul 30, 2012 · 8 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Copy link
Collaborator

spring-issuemaster commented Jul 30, 2012

Marty Jones opened SPR-9655 and commented

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.


Affects: 3.0.7

Issue Links:

  • #19075 HeaderContentNegotiationStrategy does not support multiple Accept headers

Referenced from: commits f2d7d66, 2e1a688

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

spring-issuemaster commented Aug 30, 2012

Marty Jones commented

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

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

spring-issuemaster commented Aug 30, 2012

Rossen Stoyanchev commented

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?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

spring-issuemaster commented Aug 30, 2012

Rossen Stoyanchev commented

Re-classifying from Bug to Improvement.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

spring-issuemaster commented Aug 30, 2012

Marty Jones commented

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.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

spring-issuemaster commented Aug 30, 2012

Rossen Stoyanchev commented

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.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

spring-issuemaster commented Aug 30, 2012

Marty Jones commented

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?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

spring-issuemaster commented Aug 30, 2012

Rossen Stoyanchev commented

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.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

spring-issuemaster commented Sep 18, 2012

Marty Jones commented

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());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.