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

Preserve whitespace in Content-Type header #5641

Open
jluehe opened this issue May 6, 2024 · 5 comments
Open

Preserve whitespace in Content-Type header #5641

jluehe opened this issue May 6, 2024 · 5 comments

Comments

@jluehe
Copy link

jluehe commented May 6, 2024

Has there been any consideration given to restore/preserve the whitespace character separating the charset= from the semicolon (trailing the MIME type) in the Content-Type header? Or could this be made configurable at a minimum?

See https://download.oracle.com/javaee-archive/jersey.java.net/users/2013/04/17157.html where this was brought up originally.

Apologies if this was already discussed.

Thank you!

@jansupol
Copy link
Contributor

jansupol commented May 6, 2024

I believe this is one of the tasks that should have been solvable by HeaderDelegate and associated HeaderDelegateProvider SPI, so that each customer can specify whatever he desires. The SPI has been a bit overlooked in the past, but that's the idea.
Would it help in your case?

@jluehe
Copy link
Author

jluehe commented May 6, 2024

Thank you, @jansupol! That sounds like an interesting idea! Let me play with it and see if I can get it to work!

@jluehe
Copy link
Author

jluehe commented May 13, 2024

@jansupol, I was able to pinpoint where the whitespace gets stripped in Jersey 2:

  • Jersey 1.x:
package com.sun.jersey.core.impl.provider.header;

public class MediaTypeProvider implements HeaderDelegateProvider<MediaType> {

    @Override
    public String toString(MediaType header) {
        StringBuilder b = new StringBuilder();
        b.append(header.getType()).append('/').append(header.getSubtype());
        for (Map.Entry<String, String> e : header.getParameters().entrySet()) {
            b.append("; ").append(e.getKey()).append('=');    <<<<== whitespace added after semicolon
            WriterUtil.appendQuotedIfNonToken(b, e.getValue());
        }
        return b.toString();
    }
    
    ...
}
  • Jersey 2:

https://raw.githubusercontent.com/eclipse-ee4j/jersey/2.x/core-common/src/main/java/org/glassfish/jersey/message/internal/MediaTypeProvider.java:

package org.glassfish.jersey.message.internal;

public class MediaTypeProvider implements HeaderDelegateProvider<MediaType> {

    @Override
    public String toString(MediaType header) {

        throwIllegalArgumentExceptionIfNull(header, MEDIA_TYPE_IS_NULL);

        StringBuilder b = new StringBuilder();
        b.append(header.getType()).append('/').append(header.getSubtype());
        for (Map.Entry<String, String> e : header.getParameters().entrySet()) {
            b.append(";").append(e.getKey()).append('=');    <<<<== no more whitespace added after semicolon
            StringBuilderUtils.appendQuotedIfNonToken(b, e.getValue());
        }
        return b.toString();
    }

    ...
}

@jluehe
Copy link
Author

jluehe commented May 14, 2024

@jansupol, can you tell me how I can replace/override Jersey's MediaTypeProvider with my own?

I see Jersey's RuntimeDelegateImpl getting initialized as follows:

package org.glassfish.jersey.server.internal;

public RuntimeDelegateImpl() {
    super(new MessagingBinders.HeaderDelegateProviders().getHeaderDelegateProviders());
}

where the MessagingBinders.HeaderDelegateProviders constructor uses Jersey's built-in HeaderDelegateProviders:

        public HeaderDelegateProviders() {
            Set<HeaderDelegateProvider> providers = new HashSet<>();
            providers.add(new CacheControlProvider());
            providers.add(new CookieProvider());
            providers.add(new DateProvider());
            providers.add(new EntityTagProvider());
            providers.add(new LinkProvider());
            providers.add(new LocaleProvider());
            providers.add(new MediaTypeProvider());
            providers.add(new NewCookieProvider());
            providers.add(new StringHeaderProvider());
            providers.add(new UriProvider());
            this.providers = providers;
        }

I had hoped it would also consider any custom HeaderDelegateProviders declared in META-INF/services as described here: https://eclipse-ee4j.github.io/jersey.github.io/apidocs/2.34/jersey/org/glassfish/jersey/spi/HeaderDelegateProvider.html

Do you have any recommendation for how to make this work? I noticed the same question was asked here: https://stackoverflow.com/questions/62297701/how-to-inject-custom-implementation-of-org-glassfish-jersey-spi-headerdelegatepr, but it was never answered.

Thank you!

@jluehe
Copy link
Author

jluehe commented May 14, 2024

@jansupol, what I ended up doing is extend org.glassfish.jersey.server.internal.RuntimeDelegateImpl and have my subclass (declared in META-INF/services/javax.ws.rs.ext.RuntimeDelegate) override createHeaderDelegate ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants