HTTP请求压缩

joj*_*nki 4 java compression spring gzip http

一般用例

想象一个客户端正在上传大量JSON。应该保留Content-Type,application/json因为它描述了实际数据。Accept-Encoding和Transfer-Encoding似乎是在告诉服务器应如何格式化响应。看来,响应为此目的明确使用了Content-Encoding头,但这不是有效的请求头。

我有什么想念的吗?有没有人找到一个优雅的解决方案?

具体用例

我的用例是,我有一个移动应用程序,该应用程序生成大量JSON(在某些情况下会生成一些JSON数据,但程度较小),并且压缩请求可以节省大量带宽。我使用Tomcat作为我的Servlet容器。我将Spring用于它的MVC注释,主要是为了将JEE的某些内容抽象到一个更加整洁,基于注释的接口中。我还使用Jackson进行自动(反序列化)。

我也使用nginx,但是我不确定那是否是我想要进行减压的地方。Nginx节点仅平衡请求,然后通过数据中心分发这些请求。保持压缩,直到它真正到达要处理的节点,这将是同样好的。

提前致谢,

约翰

编辑:

我和@DaSourcerer之间的讨论对撰写本文时对事物状态感到好奇的人真的很有帮助。

我最终实现了自己的解决方案。请注意,这指定了分支“ ohmage-3.0”,但是它将很快合并到master分支中。您可能要检查那里以查看我是否进行了任何更新/修复。

https://github.com/ohmage/server/blob/ohmage-3.0/src/org/ohmage/servlet/filter/DecompressionFilter.java

DaS*_*rer 8

看来 [Content-Encoding] 不是有效的请求标头。

这实际上并不完全正确。按照RFC 2616,仲14.11Content-Encoding是一实体报头,这意味着它可以在两者的实体被应用,HTTP响应和请求。通过多部分 MIME 消息的强大功能,甚至可以压缩请求(或响应)的选定部分

然而,网络服务器对压缩请求正文的支持相当有限。Apache 通过mod_deflate模块在一定程度上支持它。我并不完全清楚nginx 是否可以处理压缩请求


Hap*_*ier 5

因为原始代码不再可用。万一有人来这里需要它。我使用“ Content-Encoding:gzip”来标识是否需要解压缩的过滤器。

这是代码。

 @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
    HttpServletRequest httpServletRequest = (HttpServletRequest) request;

    String contentEncoding = httpServletRequest.getHeader("Content-Encoding");
    if (contentEncoding != null && contentEncoding.indexOf("gzip") > -1)
    {
        try
        {
            final InputStream decompressStream = StreamHelper.decompressStream(httpServletRequest.getInputStream());

            httpServletRequest = new HttpServletRequestWrapper(httpServletRequest)
            {

                @Override
                public ServletInputStream getInputStream() throws IOException
                {
                    return new DecompressServletInputStream(decompressStream);
                }

                @Override
                public BufferedReader getReader() throws IOException
                {
                    return new BufferedReader(new InputStreamReader(decompressStream));
                }
            };
        }
        catch (IOException e)
        {
            mLogger.error("error while handling the request", e);
        }
    }

    chain.doFilter(httpServletRequest, response);
}
Run Code Online (Sandbox Code Playgroud)

简单的ServletInputStream包装器类

public static class DecompressServletInputStream extends ServletInputStream
{
    private InputStream inputStream;

    public DecompressServletInputStream(InputStream input)
    {
        inputStream = input;

    }

    @Override
    public int read() throws IOException
    {
        return inputStream.read();
    }

}
Run Code Online (Sandbox Code Playgroud)

减压流代码

public class StreamHelper
{

    /**
     * Gzip magic number, fixed values in the beginning to identify the gzip
     * format <br>
     * http://www.gzip.org/zlib/rfc-gzip.html#file-format
     */
    private static final byte GZIP_ID1 = 0x1f;
    /**
     * Gzip magic number, fixed values in the beginning to identify the gzip
     * format <br>
     * http://www.gzip.org/zlib/rfc-gzip.html#file-format
     */
    private static final byte GZIP_ID2 = (byte) 0x8b;

    /**
     * Return decompression input stream if needed.
     * 
     * @param input
     *            original stream
     * @return decompression stream
     * @throws IOException
     *             exception while reading the input
     */
    public static InputStream decompressStream(InputStream input) throws IOException
    {
        PushbackInputStream pushbackInput = new PushbackInputStream(input, 2);

        byte[] signature = new byte[2];
        pushbackInput.read(signature);
        pushbackInput.unread(signature);

        if (signature[0] == GZIP_ID1 && signature[1] == GZIP_ID2)
        {
            return new GZIPInputStream(pushbackInput);
        }
        return pushbackInput;
    }
}
Run Code Online (Sandbox Code Playgroud)