복붙노트

[SPRING] HTTP 요청 압축

SPRING

HTTP 요청 압축

일반적인 사용 사례

방대한 양의 JSON을 업로드하는 클라이언트를 상상해보십시오. Content-Type은 실제 데이터를 설명하기 때문에 application / json으로 유지되어야합니다. Accept-Encoding 및 Transfer-Encoding은 서버에 응답 형식을 지정하는 방법을 알려주는 것처럼 보입니다. 응답은이 목적을 위해 명시 적으로 Content-Encoding 헤더를 사용하지만 유효한 요청 헤더가 아닌 것으로 보입니다.

제가 누락 된 것이 있습니까? 누구든지 우아한 해결책을 찾았습니까?

특정 사용 사례

필자는 많은 경우 JSON (그리고 경우에 따라서는 바이너리 데이터가 많음)을 생성하는 모바일 응용 프로그램을 사용하고 요청을 압축하면 많은 양의 대역폭이 절약된다는 것을 알 수 있습니다. 내 서블릿 컨테이너로 Tomcat을 사용하고 있습니다. 나는 MVC 애노테이션 인 Spring을 주로 JEE의 일부를 훨씬 더 깨끗한 애노테이션 기반 인터페이스로 추상화하기 위해 사용하고있다. 나는 또한 자동 (드) 직렬화를 위해 잭슨을 사용한다.

나는 또한 nginx를 사용하지만 압축 해제가 필요한 곳이 어디에 있는지 확실하지 않습니다. nginx 노드는 단순히 데이터 센터를 통해 배포되는 요청의 균형을 조정합니다. 실제로 노드를 처리 할 때까지 압축 된 상태로 유지하는 것이 좋을 것입니다.

미리 감사드립니다.

남자

편집하다:

나 자신과 @DaSourcerer 사이의 토론은이 글을 쓰는 시점의 상황에 대해 궁금한 사람들에게 정말 도움이되었습니다.

나는 내 자신의 해결책을 구현하는 것을 끝내었다. 이 명령은 "ohmage-3.0"브랜치를 지정하지만 머지 않아 master 브랜치에 병합됩니다. 내가 거기에 어떤 업데이 트 / 수정 사항을 만들 었는지 확인하고 싶을 수도 있습니다.

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

해결법

  1. ==============================

    1.사실은 사실이 아닙니다. RFC 2616, 초 14.11에 따라, Content-Encoding은 엔티티 헤더로서 http 응답과 요청 모두의 엔티티에 적용될 수 있음을 의미합니다. multipart MIME 메시지의 기능을 통해 요청 (또는 응답)의 선택된 부분까지 압축 할 수 있습니다.

    사실은 사실이 아닙니다. RFC 2616, 초 14.11에 따라, Content-Encoding은 엔티티 헤더로서 http 응답과 요청 모두의 엔티티에 적용될 수 있음을 의미합니다. multipart MIME 메시지의 기능을 통해 요청 (또는 응답)의 선택된 부분까지 압축 할 수 있습니다.

    그러나 압축 된 요청 본문에 대한 웹 서버 지원은 다소 슬림합니다. 아파치는 mod_deflate 모듈을 통해이를 어느 정도 지원한다. nginx가 압축 된 요청을 처리 할 수 ​​있는지 완전히 명확하지 않습니다.

  2. ==============================

    2.원래 코드는 더 이상 사용할 수 없으므로 누군가 여기 와서 필요하면. 나는 "Content-Encoding : gzip"을 사용하여 압축 해제 할 필터의 필요성을 확인합니다.

    원래 코드는 더 이상 사용할 수 없으므로 누군가 여기 와서 필요하면. 나는 "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);
    }
    

    간단한 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();
        }
    
    }
    

    압축 해제 스트림 코드

    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;
        }
    }
    
  3. ==============================

    3.전송할 때 헤더에 추가하십시오.

    전송할 때 헤더에 추가하십시오.

    JSON : "Accept-Encoding" : "gzip, deflate"
    

    고객 코드 :

    HttpUriRequest request = new HttpGet(url);
    request.addHeader("Accept-Encoding", "gzip");
    

    @JulianReschke는 다음과 같은 경우가있을 수 있다고 지적했다.

    "Content-Encoding" : "gzip, gzip"
    

    그래서 확장 된 서버 코드는 다음과 같습니다 :

    InputStream in = response.getEntity().getContent();
    Header encodingHeader = response.getFirstHeader("Content-Encoding");
    
    String gzip = "gzip";
    if (encodingHeader != null) {
        String encoding = encodingHeader.getValue().toLowerCase();
        int firstGzip = encoding.indexOf(gzip);
        if (firstGzip > -1) {
          in = new GZIPInputStream(in);
          int secondGzip = encoding.indexOf(gzip, firstGzip + gzip.length());
          if (secondGzip > -1) {
            in = new GZIPInputStream(in);
          }
        }
    }
    

    나는 nginx가로드 밸런서 또는 프록시로 사용되기 때문에 tomcat을 압축 해제 할 필요가 있다고 가정합니다.

    Tomcat의 server.xml에있는 Connector에 다음 속성을 추가하십시오.

    <Connector 
    compression="on"
    compressionMinSize="2048"
    compressableMimeType="text/html,application/json"
    ... />
    

    바람둥이에서 gzipped 요청을 수락하는 것은 다른 이야기입니다. 요청 압축 해제를 사용하려면 서블릿 앞에 필터를 두어야합니다. 자세한 내용은 여기에서 확인할 수 있습니다.

  4. from https://stackoverflow.com/questions/20507007/http-request-compression by cc-by-sa and MIT license