美文网首页
HttpClient 对Response gzip压缩数据的自动

HttpClient 对Response gzip压缩数据的自动

作者: 杨康他兄弟 | 来源:发表于2019-11-22 10:17 被阅读0次

    有一天,客户告诉我他们的HTTPS服务,采用了gzip方式进行压缩,因此我理所当然认为我这边需要对gzip的数据进行解压。

    方案如下:

    1,判断response header中是否有Content-Encoding 。如果为 gzip,则对其进行解压处理。

    /**
    * Get 请求
    */
    public static void sendGet(String url) throws IOException {
            SSLClient httpClient = null;
            HttpGet httpGet = null;
            CloseableHttpResponse response = null;
            String result = null;
            try {
               //这里因为是HTTPS,所以这里HttpClient有特殊处理
                httpClient = new SSLClient();
                httpGet = new HttpGet(url);
    
                Map<String, String> params = new HashMap<>(4);
                params.put("Accept-Encoding", "gzip");
                //设置header,告知服务端,本客户端支持gzip压缩格式
                setHeader(httpGet, params);
    
                response = httpClient.execute(httpGet);
    
                HttpEntity resEntity = response.getEntity();
    
                Header contentEncoding = response.getFirstHeader("Content-Encoding");
    
                if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
                    //如果返回的response header中有Content-Encoding项,且值为gzip,则进行解压处理
                    InputStream is = new GZIPInputStream(new BufferedInputStream(resEntity.getContent()));
                    StringBuffer out = new StringBuffer();
                    byte[] b = new byte[4096];
                    for (int n; (n = is.read(b)) != -1; ) {
                        out.append(new String(b, 0, n));
                    }
                    String strResult = out.toString();
                    System.out.println(strResult);
                } else {
                    if (resEntity != null) {
                        result = EntityUtils.toString(resEntity, "utf-8");
                        System.out.println(result);
                    }
                    EntityUtils.consume(resEntity);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                httpClient.close();
            }
        }
    
    
    /**
         * set HttpGet method header
         *
         * @param httpGet
         * @param params
         */
        private static void setHeader(HttpGet httpGet, Map<String, String> params) {
            if (null == httpGet) {
                throw new IllegalArgumentException("httpGet is null.");
            }
            if (!MapUtils.isEmpty(params)) {
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    httpGet.addHeader(entry.getKey(), entry.getValue());
                }
            }
        }
    

    SSLClient:

    package com.hello.https;
    
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    import org.apache.http.conn.ClientConnectionManager;
    import org.apache.http.conn.scheme.Scheme;
    import org.apache.http.conn.scheme.SchemeRegistry;
    import org.apache.http.conn.ssl.SSLSocketFactory;
    import org.apache.http.impl.client.DefaultHttpClient;
    
    public class SSLClient extends DefaultHttpClient {
        public SSLClient() throws Exception{
            super();
            SSLContext ctx = SSLContext.getInstance("TLS");
    
            X509TrustManager tm = new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain,
                                               String authType) throws CertificateException {
                }
                @Override
                public void checkServerTrusted(X509Certificate[] chain,
                                               String authType) throws CertificateException {
                }
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
    
            ctx.init(null, new TrustManager[]{tm}, null);
            SSLSocketFactory ssf = new SSLSocketFactory(ctx,SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            ClientConnectionManager ccm = this.getConnectionManager();
            SchemeRegistry sr = ccm.getSchemeRegistry();
            sr.register(new Scheme("https", 443, ssf));
        }
    }
    
    

    2,可以利用HttpClient中的ResponseContentEncoding进行自动解压

    /*
     * ====================================================================
     * Licensed to the Apache Software Foundation (ASF) under one
     * or more contributor license agreements.  See the NOTICE file
     * distributed with this work for additional information
     * regarding copyright ownership.  The ASF licenses this file
     * to you under the Apache License, Version 2.0 (the
     * "License"); you may not use this file except in compliance
     * with the License.  You may obtain a copy of the License at
     *
     *   http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied.  See the License for the
     * specific language governing permissions and limitations
     * under the License.
     * ====================================================================
     *
     * This software consists of voluntary contributions made by many
     * individuals on behalf of the Apache Software Foundation.  For more
     * information on the Apache Software Foundation, please see
     * <http://www.apache.org/>.
     *
     */
    package org.apache.http.client.protocol;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Locale;
    import java.util.zip.GZIPInputStream;
    
    import org.apache.http.Header;
    import org.apache.http.HeaderElement;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpException;
    import org.apache.http.HttpResponse;
    import org.apache.http.HttpResponseInterceptor;
    import org.apache.http.annotation.Immutable;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.entity.DecompressingEntity;
    import org.apache.http.client.entity.DeflateInputStream;
    import org.apache.http.client.entity.InputStreamFactory;
    import org.apache.http.config.Lookup;
    import org.apache.http.config.RegistryBuilder;
    import org.apache.http.protocol.HttpContext;
    
    /**
     * {@link HttpResponseInterceptor} responsible for processing Content-Encoding
     * responses.
     * <p>
     * Instances of this class are stateless and immutable, therefore threadsafe.
     *
     * @since 4.1
     *
     */
    @Immutable
    public class ResponseContentEncoding implements HttpResponseInterceptor {
    
        public static final String UNCOMPRESSED = "http.client.response.uncompressed";
    
        private final static InputStreamFactory GZIP = new InputStreamFactory() {
    
            @Override
            public InputStream create(final InputStream instream) throws IOException {
                return new GZIPInputStream(instream);
            }
        };
    
        private final static InputStreamFactory DEFLATE = new InputStreamFactory() {
    
            @Override
            public InputStream create(final InputStream instream) throws IOException {
                return new DeflateInputStream(instream);
            }
    
        };
    
        private final Lookup<InputStreamFactory> decoderRegistry;
        private final boolean ignoreUnknown;
    
        /**
         * @since 4.5
         */
        public ResponseContentEncoding(final Lookup<InputStreamFactory> decoderRegistry, final boolean ignoreUnknown) {
            this.decoderRegistry = decoderRegistry != null ? decoderRegistry :
                RegistryBuilder.<InputStreamFactory>create()
                        .register("gzip", GZIP)
                        .register("x-gzip", GZIP)
                        .register("deflate", DEFLATE)
                        .build();
            this.ignoreUnknown = ignoreUnknown;
        }
    
        /**
         * @since 4.5
         */
        public ResponseContentEncoding(final boolean ignoreUnknown) {
            this(null, ignoreUnknown);
        }
    
        /**
         * @since 4.4
         */
        public ResponseContentEncoding(final Lookup<InputStreamFactory> decoderRegistry) {
            this(decoderRegistry, true);
        }
    
        /**
         * Handles {@code gzip} and {@code deflate} compressed entities by using the following
         * decoders:
         * <ul>
         * <li>gzip - see {@link GZIPInputStream}</li>
         * <li>deflate - see {@link DeflateInputStream}</li>
         * </ul>
         */
        public ResponseContentEncoding() {
            this(null);
        }
    
        @Override
        public void process(
                final HttpResponse response,
                final HttpContext context) throws HttpException, IOException {
            final HttpEntity entity = response.getEntity();
    
            final HttpClientContext clientContext = HttpClientContext.adapt(context);
            final RequestConfig requestConfig = clientContext.getRequestConfig();
            // entity can be null in case of 304 Not Modified, 204 No Content or similar
            // check for zero length entity.
            if (requestConfig.isContentCompressionEnabled() && entity != null && entity.getContentLength() != 0) {
                final Header ceheader = entity.getContentEncoding();
                if (ceheader != null) {
                    final HeaderElement[] codecs = ceheader.getElements();
                    for (final HeaderElement codec : codecs) {
                        final String codecname = codec.getName().toLowerCase(Locale.ROOT);
                        final InputStreamFactory decoderFactory = decoderRegistry.lookup(codecname);
                        if (decoderFactory != null) {
                            response.setEntity(new DecompressingEntity(response.getEntity(), decoderFactory));
                            response.removeHeaders("Content-Length");
                            response.removeHeaders("Content-Encoding");
                            response.removeHeaders("Content-MD5");
                        } else {
                            if (!"identity".equals(codecname) && !ignoreUnknown) {
                                throw new HttpException("Unsupported Content-Encoding: " + codec.getName());
                            }
                        }
                    }
                }
            }
        }
    }
    
    

    我们在获取HttpClient对象时,通过如下方式即可创建能自动处理服务器端进行压缩过的数据的HttpClient

    CloseableHttpClient httpclient = HttpClients.custom()
                    .setSSLSocketFactory(sslsf) //这里因为要请求的是https服务,所以这样处理,后面会写文介绍此处
                    .build();
    

    为什么要这样操作呢?那是因为HttpClients.custom()方法会返回一个HttpClientBuilder对象,这个HttpClientBuilder对象用于构建httpclient 对象。我们不妨看看HttpClientBuilder类的build方法:

    public CloseableHttpClient build() {
            // Create main request executor
            // We copy the instance fields to avoid changing them, and rename to avoid accidental use of the wrong version
            PublicSuffixMatcher publicSuffixMatcherCopy = this.publicSuffixMatcher;
            if (publicSuffixMatcherCopy == null) {
                publicSuffixMatcherCopy = PublicSuffixMatcherLoader.getDefault();
            }
    
            HttpRequestExecutor requestExecCopy = this.requestExec;
            if (requestExecCopy == null) {
                requestExecCopy = new HttpRequestExecutor();
            }
            HttpClientConnectionManager connManagerCopy = this.connManager;
            if (connManagerCopy == null) {
                LayeredConnectionSocketFactory sslSocketFactoryCopy = this.sslSocketFactory;
                if (sslSocketFactoryCopy == null) {
                    final String[] supportedProtocols = systemProperties ? split(
                            System.getProperty("https.protocols")) : null;
                    final String[] supportedCipherSuites = systemProperties ? split(
                            System.getProperty("https.cipherSuites")) : null;
                    HostnameVerifier hostnameVerifierCopy = this.hostnameVerifier;
                    if (hostnameVerifierCopy == null) {
                        hostnameVerifierCopy = new DefaultHostnameVerifier(publicSuffixMatcherCopy);
                    }
                    if (sslContext != null) {
                        sslSocketFactoryCopy = new SSLConnectionSocketFactory(
                                sslContext, supportedProtocols, supportedCipherSuites, hostnameVerifierCopy);
                    } else {
                        if (systemProperties) {
                            sslSocketFactoryCopy = new SSLConnectionSocketFactory(
                                    (SSLSocketFactory) SSLSocketFactory.getDefault(),
                                    supportedProtocols, supportedCipherSuites, hostnameVerifierCopy);
                        } else {
                            sslSocketFactoryCopy = new SSLConnectionSocketFactory(
                                    SSLContexts.createDefault(),
                                    hostnameVerifierCopy);
                        }
                    }
                }
                @SuppressWarnings("resource")
                final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager(
                        RegistryBuilder.<ConnectionSocketFactory>create()
                            .register("http", PlainConnectionSocketFactory.getSocketFactory())
                            .register("https", sslSocketFactoryCopy)
                            .build(),
                        null,
                        null,
                        null,
                        connTimeToLive,
                        connTimeToLiveTimeUnit != null ? connTimeToLiveTimeUnit : TimeUnit.MILLISECONDS);
                if (defaultSocketConfig != null) {
                    poolingmgr.setDefaultSocketConfig(defaultSocketConfig);
                }
                if (defaultConnectionConfig != null) {
                    poolingmgr.setDefaultConnectionConfig(defaultConnectionConfig);
                }
                if (systemProperties) {
                    String s = System.getProperty("http.keepAlive", "true");
                    if ("true".equalsIgnoreCase(s)) {
                        s = System.getProperty("http.maxConnections", "5");
                        final int max = Integer.parseInt(s);
                        poolingmgr.setDefaultMaxPerRoute(max);
                        poolingmgr.setMaxTotal(2 * max);
                    }
                }
                if (maxConnTotal > 0) {
                    poolingmgr.setMaxTotal(maxConnTotal);
                }
                if (maxConnPerRoute > 0) {
                    poolingmgr.setDefaultMaxPerRoute(maxConnPerRoute);
                }
                connManagerCopy = poolingmgr;
            }
            ConnectionReuseStrategy reuseStrategyCopy = this.reuseStrategy;
            if (reuseStrategyCopy == null) {
                if (systemProperties) {
                    final String s = System.getProperty("http.keepAlive", "true");
                    if ("true".equalsIgnoreCase(s)) {
                        reuseStrategyCopy = DefaultConnectionReuseStrategy.INSTANCE;
                    } else {
                        reuseStrategyCopy = NoConnectionReuseStrategy.INSTANCE;
                    }
                } else {
                    reuseStrategyCopy = DefaultConnectionReuseStrategy.INSTANCE;
                }
            }
            ConnectionKeepAliveStrategy keepAliveStrategyCopy = this.keepAliveStrategy;
            if (keepAliveStrategyCopy == null) {
                keepAliveStrategyCopy = DefaultConnectionKeepAliveStrategy.INSTANCE;
            }
            AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy;
            if (targetAuthStrategyCopy == null) {
                targetAuthStrategyCopy = TargetAuthenticationStrategy.INSTANCE;
            }
            AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy;
            if (proxyAuthStrategyCopy == null) {
                proxyAuthStrategyCopy = ProxyAuthenticationStrategy.INSTANCE;
            }
            UserTokenHandler userTokenHandlerCopy = this.userTokenHandler;
            if (userTokenHandlerCopy == null) {
                if (!connectionStateDisabled) {
                    userTokenHandlerCopy = DefaultUserTokenHandler.INSTANCE;
                } else {
                    userTokenHandlerCopy = NoopUserTokenHandler.INSTANCE;
                }
            }
    
            String userAgentCopy = this.userAgent;
            if (userAgentCopy == null) {
                if (systemProperties) {
                    userAgentCopy = System.getProperty("http.agent");
                }
                if (userAgentCopy == null) {
                    userAgentCopy = VersionInfo.getUserAgent("Apache-HttpClient",
                            "org.apache.http.client", getClass());
                }
            }
    
            ClientExecChain execChain = createMainExec(
                    requestExecCopy,
                    connManagerCopy,
                    reuseStrategyCopy,
                    keepAliveStrategyCopy,
                    new ImmutableHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)),
                    targetAuthStrategyCopy,
                    proxyAuthStrategyCopy,
                    userTokenHandlerCopy);
    
            execChain = decorateMainExec(execChain);
    
            HttpProcessor httpprocessorCopy = this.httpprocessor;
            if (httpprocessorCopy == null) {
    
                final HttpProcessorBuilder b = HttpProcessorBuilder.create();
                if (requestFirst != null) {
                    for (final HttpRequestInterceptor i: requestFirst) {
                        b.addFirst(i);
                    }
                }
                if (responseFirst != null) {
                    for (final HttpResponseInterceptor i: responseFirst) {
                        b.addFirst(i);
                    }
                }
                b.addAll(
                        new RequestDefaultHeaders(defaultHeaders),
                        new RequestContent(),
                        new RequestTargetHost(),
                        new RequestClientConnControl(),
                        new RequestUserAgent(userAgentCopy),
                        new RequestExpectContinue());
                if (!cookieManagementDisabled) {
                    b.add(new RequestAddCookies());
                }
                if (!contentCompressionDisabled) {
                    if (contentDecoderMap != null) {
                        final List<String> encodings = new ArrayList<String>(contentDecoderMap.keySet());
                        Collections.sort(encodings);
                        b.add(new RequestAcceptEncoding(encodings));
                    } else {
                        b.add(new RequestAcceptEncoding());
                    }
                }
                if (!authCachingDisabled) {
                    b.add(new RequestAuthCache());
                }
                if (!cookieManagementDisabled) {
                    b.add(new ResponseProcessCookies());
                }
                if (!contentCompressionDisabled) {
                    if (contentDecoderMap != null) {
                        final RegistryBuilder<InputStreamFactory> b2 = RegistryBuilder.create();
                        for (Map.Entry<String, InputStreamFactory> entry: contentDecoderMap.entrySet()) {
                            b2.register(entry.getKey(), entry.getValue());
                        }
                        b.add(new ResponseContentEncoding(b2.build()));
                    } else {
                        b.add(new ResponseContentEncoding());
                    }
                }
                if (requestLast != null) {
                    for (final HttpRequestInterceptor i: requestLast) {
                        b.addLast(i);
                    }
                }
                if (responseLast != null) {
                    for (final HttpResponseInterceptor i: responseLast) {
                        b.addLast(i);
                    }
                }
                httpprocessorCopy = b.build();
            }
            execChain = new ProtocolExec(execChain, httpprocessorCopy);
    
            execChain = decorateProtocolExec(execChain);
    
            // Add request retry executor, if not disabled
            if (!automaticRetriesDisabled) {
                HttpRequestRetryHandler retryHandlerCopy = this.retryHandler;
                if (retryHandlerCopy == null) {
                    retryHandlerCopy = DefaultHttpRequestRetryHandler.INSTANCE;
                }
                execChain = new RetryExec(execChain, retryHandlerCopy);
            }
    
            HttpRoutePlanner routePlannerCopy = this.routePlanner;
            if (routePlannerCopy == null) {
                SchemePortResolver schemePortResolverCopy = this.schemePortResolver;
                if (schemePortResolverCopy == null) {
                    schemePortResolverCopy = DefaultSchemePortResolver.INSTANCE;
                }
                if (proxy != null) {
                    routePlannerCopy = new DefaultProxyRoutePlanner(proxy, schemePortResolverCopy);
                } else if (systemProperties) {
                    routePlannerCopy = new SystemDefaultRoutePlanner(
                            schemePortResolverCopy, ProxySelector.getDefault());
                } else {
                    routePlannerCopy = new DefaultRoutePlanner(schemePortResolverCopy);
                }
            }
            // Add redirect executor, if not disabled
            if (!redirectHandlingDisabled) {
                RedirectStrategy redirectStrategyCopy = this.redirectStrategy;
                if (redirectStrategyCopy == null) {
                    redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE;
                }
                execChain = new RedirectExec(execChain, routePlannerCopy, redirectStrategyCopy);
            }
    
            // Optionally, add service unavailable retry executor
            final ServiceUnavailableRetryStrategy serviceUnavailStrategyCopy = this.serviceUnavailStrategy;
            if (serviceUnavailStrategyCopy != null) {
                execChain = new ServiceUnavailableRetryExec(execChain, serviceUnavailStrategyCopy);
            }
            // Optionally, add connection back-off executor
            if (this.backoffManager != null && this.connectionBackoffStrategy != null) {
                execChain = new BackoffStrategyExec(execChain, this.connectionBackoffStrategy, this.backoffManager);
            }
    
            Lookup<AuthSchemeProvider> authSchemeRegistryCopy = this.authSchemeRegistry;
            if (authSchemeRegistryCopy == null) {
                authSchemeRegistryCopy = RegistryBuilder.<AuthSchemeProvider>create()
                    .register(AuthSchemes.BASIC, new BasicSchemeFactory())
                    .register(AuthSchemes.DIGEST, new DigestSchemeFactory())
                    .register(AuthSchemes.NTLM, new NTLMSchemeFactory())
                    .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
                    .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory())
                    .build();
            }
            Lookup<CookieSpecProvider> cookieSpecRegistryCopy = this.cookieSpecRegistry;
            if (cookieSpecRegistryCopy == null) {
                cookieSpecRegistryCopy = CookieSpecRegistries.createDefault(publicSuffixMatcherCopy);
            }
    
            CookieStore defaultCookieStore = this.cookieStore;
            if (defaultCookieStore == null) {
                defaultCookieStore = new BasicCookieStore();
            }
    
            CredentialsProvider defaultCredentialsProvider = this.credentialsProvider;
            if (defaultCredentialsProvider == null) {
                if (systemProperties) {
                    defaultCredentialsProvider = new SystemDefaultCredentialsProvider();
                } else {
                    defaultCredentialsProvider = new BasicCredentialsProvider();
                }
            }
    
            List<Closeable> closeablesCopy = closeables != null ? new ArrayList<Closeable>(closeables) : null;
            if (!this.connManagerShared) {
                if (closeablesCopy == null) {
                    closeablesCopy = new ArrayList<Closeable>(1);
                }
                final HttpClientConnectionManager cm = connManagerCopy;
    
                if (evictExpiredConnections || evictIdleConnections) {
                    final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(cm,
                            maxIdleTime > 0 ? maxIdleTime : 10, maxIdleTimeUnit != null ? maxIdleTimeUnit : TimeUnit.SECONDS);
                    closeablesCopy.add(new Closeable() {
    
                        @Override
                        public void close() throws IOException {
                            connectionEvictor.shutdown();
                        }
    
                    });
                    connectionEvictor.start();
                }
                closeablesCopy.add(new Closeable() {
    
                    @Override
                    public void close() throws IOException {
                        cm.shutdown();
                    }
    
                });
            }
    
            return new InternalHttpClient(
                    execChain,
                    connManagerCopy,
                    routePlannerCopy,
                    cookieSpecRegistryCopy,
                    authSchemeRegistryCopy,
                    defaultCookieStore,
                    defaultCredentialsProvider,
                    defaultRequestConfig != null ? defaultRequestConfig : RequestConfig.DEFAULT,
                    closeablesCopy);
        }
    
    其中关键点是 image.png

    我们在看看new ResponseContentEncoding():

    会发现这里: image.png
    这里有对gzip压缩格式进行处理。
    HttpClientBuilder中 image.png
    HttpProcessorBuilder image.png
    我们可以看到其有两种类型的拦截器链,一个是用于请求的request,一个是用于响应的response,而ResponseContentEncoding就是response中的一个拦截器,用于处理压缩数据的。
    综上,我自己推测,HttpClient通过这种方法自己设置了用于解压服务器端压缩过的gzip数据。

    相关文章

      网友评论

          本文标题:HttpClient 对Response gzip压缩数据的自动

          本文链接:https://www.haomeiwen.com/subject/pfefwctx.html