How to serve already gzipped content in JAX-RS?

余生长醉 提交于 2019-12-24 03:38:06

问题


I'm developing a small JAX-RS application with Resteasy. I wanted the application to serve some static content for Javascript and CSS files, etc. and I would like to take advantage of the already gzipped version of the resources packaged in the jars of webjars.org. Thus, I need to handle the Accept-Encoding header and check if the .gz is there (or not).

So far, what I have is:

@Path("res/{path:.*}")
@GET
public Response webjars(@PathParam("path") String path, @HeaderParam("Accept-Encoding") String acceptEncoding) {

    // Guesses MIME type from the path extension elsewhere.
    String mime = mimes.getContentType(path);

    if (acceptEncoding.contains("gzip")) {
        InputStream is = getClass().getResourceAsStream("/META-INF/resources/webjars/" + path + ".gz");
        if (is != null)
            return Response.ok().type(mime).encoding("gzip").entity(is).build();
    }

    InputStream is = getClass().getResourceAsStream("/META-INF/resources/webjars/" + path);
    if (is != null)
        return Response.ok().type(mime).entity(is).build();

    return Response.status(Status.NOT_FOUND).build();
}

But it doesn't work. The content served is totally broken. So far, I've found that a component that compresses the stream again: org.jboss.resteasy.plugins.interceptors.encoding.GZIPEncodingInterceptor because I manually filled the Content-Encoding header (using the ResponseBuilder.encoding method).

This looks like a bug to me because, apparently, there's no way to share an already gzipped stream. However, Is this achievable using JAX-RS? Is this a Resteasy bug?

I can think of a variety of ways to achieve the same thing externally to Resteasy, like mapping the webjars.org servlet (I'm not in a Servlet API 3.0 environment, so I have no META-INF/resources/ automatic classpath mapping). Nevertheless, my questions still prevail. It applies to several other scenarios.

Update:

For the record I have filled the issue RESTEASY-1170.


回答1:


Here's an example implementation of my above comment.

The point I'm getting at is that if you don't want it to be handle by the current interceptor, don't set the header, create an Interceptor that will be name binded, with your own annotation, and set the priority to one lower than the one you want to avoid, then set the header in your Interceptor...

@AlreadyGzipped

@NameBinding
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AlreadyGzipped {}

WriterInterceptor. Notice the @Priority. The GZIPEncodingInterceptor uses Priorities.ENTITY_CODER

@Provider
@AlreadyGzipped
@Priority(Priorities.ENTITY_CODER + 1000)
public class AlreadyGzippedWriterInterceptor implements WriterInterceptor {
    @Context HttpHeaders headers;

    @Override
    public void aroundWriteTo(WriterInterceptorContext wic) throws IOException, 
                                                      WebApplicationException {
        String header = headers.getHeaderString("Accept-Encoding");
        if (null != header && header.equalsIgnoreCase("gzip")) {
            wic.getHeaders().putSingle("Content-Encoding", "gzip");
        }
        wic.proceed();
    }  
}

Test resource

@Path("resource")
public class AlreadyGzippedResoure {

    @GET
    @AlreadyGzipped
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    public Response getAlreadGzipped() throws Exception {
        InputStream is = getClass().getResourceAsStream("/stackoverflow.png.gz");
        return Response.ok(is).build();
    }
}

Test

public class Main {
    public static void main(String[] args) throws Exception {
        Client client = ClientBuilder.newClient();
        String url = "http://localhost:8080/api/resource";

        Response response = client.target(url).request().acceptEncoding("gzip").get();
        Image image = ImageIO.read(response.readEntity(InputStream.class));
        JOptionPane.showMessageDialog(null,new JLabel(new ImageIcon(image)));
    }
}

Result



来源:https://stackoverflow.com/questions/29653641/how-to-serve-already-gzipped-content-in-jax-rs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!