Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Jersey swallow my "Content-Encoding" header

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.

like image 283
Rafael Winterhalter Avatar asked Nov 05 '13 16:11

Rafael Winterhalter


People also ask

What is content encoding in header?

The Content-Encoding representation header lists any encodings that have been applied to the representation (message payload), and in what order. This lets the recipient know how to decode the representation in order to obtain the original payload format.

Who sets accept Encoding header?

All the HTTP client used to tell the server which encoding or encoding it supports. Then the server will respond in any of the supporting encoding formats. The server selects any one of the proposals, uses it and informs the client of its choice with the Content-Encoding response header.

What is accept encoding in HTTP header?

The Accept-Encoding request HTTP header indicates the content encoding (usually a compression algorithm) that the client can understand. The server uses content negotiation to select one of the proposals and informs the client of that choice with the Content-Encoding response header.


1 Answers

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...)

like image 159
Marek Potociar Avatar answered Oct 12 '22 23:10

Marek Potociar