You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java客户端向Apache发送GZIP压缩请求报400错误求助

双向HTTP Gzip压缩请求报错排查与解决

问题现象

需要通过Gzip内容编码HTTP头实现请求与响应的双向HTTP流量压缩:

  • 已实现接收并解压Apache HTTP Server返回的压缩数据
  • 向服务器发送GZIP格式的压缩请求时,收到400 Bad Request错误,Apache日志显示:AH01387: Zlib: Invalid header

Java客户端代码

public class HttpClientTester extends StandaloneApplication {
    public static byte[] deflate(byte[] input) {
        Deflater deflater = new Deflater();
        deflater.setLevel(9);
        deflater.setInput(input);
        deflater.finish();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        while (!deflater.finished()) {
            int compressedSize = deflater.deflate(buffer);
            outputStream.write(buffer, 0, compressedSize);
        }
        return outputStream.toByteArray();
    }

    public static byte[] compress(byte[] input) throws IOException {
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        GZIPOutputStream zipStream = new GZIPOutputStream(byteStream);
        zipStream.write(input);
        zipStream.flush();
        zipStream.finish();
        zipStream.close();
        return byteStream.toByteArray();
    }

    public static String deflateReverse(byte[] input) throws DataFormatException, UnsupportedEncodingException {
        Inflater inflater = new Inflater();
        inflater.setInput(input);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        while (!inflater.finished()) {
            int decompressedSize = inflater.inflate(buffer);
            outputStream.write(buffer, 0, decompressedSize);
        }
        return new String(outputStream.toByteArray(), "UTF-8");
    }

    public static String decompress(byte[] input) throws IOException {
        GZIPInputStream zippedInputStream = new GZIPInputStream(new ByteArrayInputStream(input));
        return Tools.readStream(zippedInputStream, StandardCharsets.UTF_8);
    }

    @Override
    public Void execute(StandaloneApplicationContext arg0) throws Exception {
        Bundle sslProperties = new Bundle();
        sslProperties.set(SSLProperties.SSL_VERSION, "TLSv1.2");
        sslProperties.set(SSLProperties.SSL_TRUST_ALL_CERTS, true);
        HTTPSHandler httpsHandler = new HTTPSHandler(null, arg0.getLoggingContext().getLoggingId(), sslProperties);
        HttpClientV2 clientV2 = new HttpClientV2(null, arg0.getLoggingContext().getLoggingId(), arg0.getConfigurationContext().getParametersReader());
        clientV2.setSSLHandler(httpsHandler);
        HttpRequest httpRequest = HttpFactory.createHttpRequest(HttpConstants.HTTP_CONTENT_TYPE_TEXT_HTML);
        httpRequest.setTransactionId(arg0.getLoggingContext().getLoggingId());
        httpRequest.setHeaderField("Accept-encoding", "gzip");
        httpRequest.setHeaderField("Content-encoding", "gzip");
        httpRequest.setURL("https://myserver/myapp");
        httpRequest.setMethod(HttpMethod.POST);
        byte[] unzippedInputBody = "<MessageEnvelop><Type>ERROR</Type><Anomaly>ERROR WHILE PARSING XML REQUEST</Anomaly></MessageEnvelop>".getBytes("UTF-8");
        byte[] zippedInputBody = compress(unzippedInputBody);
        httpRequest.setBody(zippedInputBody);
        httpRequest.setHeaderField("content-length", "" + zippedInputBody.length);
        HttpResponse httpResponse = clientV2.doCommunication(httpRequest);
        byte[] stream = httpResponse.getBodyStream();
        String data = decompress(stream);
        //@formatter:off
        pause(
            "Data length.........: " + stream.length + "\n"+ 
            "Real data...........: " + data + "\n"+
            "Response B64 data...: " + new Base64().encode(stream) + "\n"+
            "Request B64 data....: " + new Base64().encode(zippedInputBody));
        //@formatter:on
        return null;
    }

    public static void main(String[] args) {
        StandaloneApplicationContext.run(new HttpClientTester());
    }
}

Apache VirtualHost配置

SetInputFilter DEFLATE
SetOutputFilter DEFLATE

相关压缩数据(Base64格式)

H4sIAAAAAAAAALPxTS0uTkxPdc0rS83JL7CzCaksSLVzDQryD7LRB7NtHPPycxNzKiGCCuEenj6uCgGOQcGefu4KEb4+CkGugaGuwSE2+jCFNvpohgIA/IH0c2UAAAA=


问题原因

Apache的SetInputFilter DEFLATE过滤器期望接收的是纯DEFLATE格式的压缩数据,而非GZIP格式:

  • GZIP格式在DEFLATE压缩流基础上,额外添加了10字节的头部(包含标识、压缩方法等)和8字节的尾部(CRC32校验值、原始数据长度)
  • 客户端通过GZIPOutputStream生成的是完整GZIP格式数据,不符合Apache输入过滤器的解析要求,因此触发"无效头"错误

解决方案

方案1:修改客户端使用DEFLATE格式压缩请求(推荐)

  1. 使用现有deflate方法生成纯DEFLATE压缩流,替换原compress方法的调用
  2. 将请求头Content-Encoding改为deflate(匹配Apache过滤器的期望)

修改后的execute方法关键部分:

// 替换压缩逻辑为deflate方法
byte[] zippedInputBody = deflate(unzippedInputBody);
// 修改Content-Encoding头
httpRequest.setHeaderField("Content-encoding", "deflate");

方案2:修改Apache配置支持GZIP格式请求(复杂)

Apache原生DEFLATE输入过滤器不支持GZIP格式,需通过以下方式实现:

  • 安装第三方模块(如mod_gzip,但已停止维护)
  • 编写自定义Apache模块或使用Lua脚本解析GZIP格式请求

此方案复杂度高,不推荐用于简单场景。


内容的提问来源于stack exchange,提问作者Francesco Rizzi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 00:32:04