Why does Jersey swallow my "Content-Encoding" header
Asked Answered
B

1

5

Why does the following example swallow my HTTP-Header for "Content-Encoding" in the request. I am writing an application where I need to decode a custom encoding format. However, I can never get hold of the "Content-Encoding" header from the request. Neither in the actual resource nor in an ReaderInterceptor. In the response, this encoding header is not swallowed.

This behavior can be easily observed in the following (runnable) example:

public class Demo extends JerseyTest {
  @Override
  protected Application configure() {
    enable(TestProperties.DUMP_ENTITY);
    enable(TestProperties.LOG_TRAFFIC);
    return new ResourceConfig(MyResource.class, MyInterceptor.class);
  }

  public static final String PATH = "path";
  public static final String ENCODING = "my-encoding";
  public static final String CUSTOM_HEADER = "X-Content-Encoding";
  public static final String QUESTION = "question", ANSWER = "answer";

  @Path(PATH)
  public static class MyResource {
    @POST
    public Response handle(String value, @Context HttpHeaders httpHeaders) {
      assertEquals(ENCODING, httpHeaders.getHeaderString(CUSTOM_HEADER));
      // Here, the "Content-Encoding" header mysteriously disappeared.
      assertEquals(ENCODING, httpHeaders.getHeaderString(HttpHeaders.CONTENT_ENCODING));
      return Response
          .ok(ANSWER)
          .header(CUSTOM_HEADER, ENCODING)
          .header(HttpHeaders.CONTENT_ENCODING, ENCODING)
          .build();
    }
  }

  public static class MyInterceptor implements ReaderInterceptor, WriterInterceptor {
    @Override
    public Object aroundReadFrom(ReaderInterceptorContext context) 
        throws IOException, WebApplicationException {
      assertEquals(ENCODING, context.getHeaders().getFirst(CUSTOM_HEADER));
      // Here, the "Content-Encoding" header mysteriously disappeared.
      assertEquals(ENCODING, context.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING));
      return context.proceed();
    }

    @Override
    public void aroundWriteTo(WriterInterceptorContext context) 
        throws IOException, WebApplicationException {
      assertEquals(ENCODING, context.getHeaders().getFirst(CUSTOM_HEADER));
      // Here, the "Content-Encoding" header can be found.
      assertEquals(ENCODING, context.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING));
      context.proceed();
    }
  }

  @Test
  public void test() throws Exception {
    Response response = target(PATH)
        .request()
        .header(CUSTOM_HEADER, ENCODING)
        .header(HttpHeaders.CONTENT_ENCODING, ENCODING)
        .post(Entity.text(QUESTION));
    assertEquals(200, response.getStatus());
    assertEquals(ENCODING, response.getHeaders().getFirst(CUSTOM_HEADER));
    // Here, the "Content-Encoding" header can be found.
    assertEquals(ENCODING, response.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING));
  }
}

Is there some magic happening behind the curtains where Jersey tries to fix up my content encoding? (What it cannot since its a closed-source encoding which I must as a matter of fact resolve by querying another application on another server in the network.) I cannot even discover the "Content-Encoding" header in the request dump which is why I suspect Jersey to not send the header at all.

I could of course use some "X-Content-Encoding" header, this works as demonstrated in the example. But this solution is just dumb. I already searched the various CommonProperties, ServerProperties, ClientProperties constant pools but I did not find a configuration option.

Breeks answered 5/11, 2013 at 16:34 Comment(0)
T
8

The problem you see is because you are effectively overwriting the Content-Encoding header with the

.post(Entity.text(QUESTION));

call. The Entity.text(...) method produces entity with the content data Variant fields set to:

media type = "text/plain";
content language = null;
content encoding = null;

These null values of content encoding and language in turn erase any previously set Content-Encoding or Content-Language headers. To fix this, you need to specify the content encoding as part of your entity:

    Response response = target(PATH)
            .request()
            .post(Entity.entity(QUESTION, 
                  new Variant(MediaType.TEXT_PLAIN_TYPE, (String) null, "my-encoding")));

(I agree this behavior is somewhat confusing as it is not obvious. Perhaps we should fix Jersey to not override headers with null variant field values if set...)

Tigre answered 5/11, 2013 at 19:8 Comment(4)
Unfortunately, it does not seem that changing the overwriting behavior will be straightforward in Jersey. The JAX-RS javadoc for the post method says clearly: Any variant-related HTTP headers previously set (namely Content-Type, Content-Language and Content-Encoding) will be overwritten using the entity variant information.Tigre
I agree with what you suggested. In my application, I am using the Entity for specifying a custom MediaType. I always wondered why there was no explicit method for setting the content encoding, but I guess this is why. Thank you!Breeks
For future readers: I added a WriterInterceptor to my unit test via JerseyTest#configureClient(ClientConfig). In doing so, I avoid a lot of boiler plate. Since the interceptor is called after the invocation of RequestBuilder#post(Entitiy), I can add the request header and overwrite the one implicitly set when the Entitiy is resolved.Breeks
However, using this outside my unit test did not work: #19813467Breeks

© 2022 - 2024 — McMap. All rights reserved.