我正在使用新的java.net.http类来处理异步HTTP请求和响应交换,并且我试图找到一种方法使BodySubscriber处理不同的编码类型,例如gzip。

但是,映射BodySubsriber<InputStream>以便基础流被GZIPInputStream包装(当在响应标头中找到“Content-Encoding:gzip”时)会导致挂起。没有例外,只是完全停止 Activity 。

映射BodySubscriber的代码如下所示:

private HttpResponse.BodySubscriber<InputStream> gzippedBodySubscriber(
        HttpResponse.ResponseInfo responseInfo) {
    return HttpResponse.BodySubscribers.mapping(
            HttpResponse.BodySubscribers.ofInputStream(),
            this::decodeGzipStream);
}

private InputStream decodeGzipStream(InputStream gzippedStream) {
    System.out.println("Entered decodeGzipStream method.");
    try {
        InputStream decodedStream = new GZIPInputStream(gzippedStream);
        System.out.println(
                "Created GZIPInputStream to handle response body stream.");
        return decodedStream;
    } catch (IOException ex) {
        System.out.println("IOException occurred while trying to create GZIPInputStream.");
        throw new UncheckedIOException(ex);
    }
}

接收到具有“gzip”编码的HTTP响应,控制台将显示以下内容:

输入了EncodedBodyHandler.apply方法。
输入了encodeGzipStream方法。

什么也看不到,因此永远不会执行对GZIPInputStream构造函数的调用之后的行。

有谁知道为什么这种尝试将InputStream中的BodySubscriber<InputStream>包装在GZIPInputStream中的尝试挂起了?

注意:未编码(原始文本)HTTP响应正文的等效方法仅包含对BodySubscribers.ofInputStream()的调用,而没有映射,这使得可以毫无问题地接收和显示响应。

最佳答案

这确实是一个错误。我已经记录了JDK-8217264。我可以建议两个解决方法:

解决方法一

不要使用BodySubscribers.mapping-在获取HttpResponse的主体之后,将InputStream转换为GZIPInputStream:

GZIPInputStream gzin = new GZIPInputStream(resp.getBody());

解决方法二

让映射函数返回Supplier<InputStream>,注意不要在调用GZIPInputStream之前创建Supplier::get
static final class ISS implements Supplier<InputStream> {
    final InputStream in;
    GZIPInputStream gz;
    ISS(InputStream in) {
        this.in = in;
    }
    public synchronized InputStream get() {
        if (gz == null) {
            try {
                gz = new GZIPInputStream(in);
            } catch (IOException t) {
                throw new UncheckedIOException(t);
            }
        }
        return gz;
    }
}

09-12 06:09