代码之家  ›  专栏  ›  技术社区  ›  Bobulous

在gzip输入流中包装bodysubscriber<inputstream>将导致挂起

  •  2
  • Bobulous  · 技术社区  · 5 年前

    我用的是新的 java.net.http 类来处理异步HTTP请求+响应交换,我试图找到一种方法让bodysubscriber处理不同的编码类型,如gzip。

    但是,映射 BodySubsriber<InputStream> 使底层流被 GZIPInputStream (当在响应头中找到“content-encoding:gzip”时)导致挂起。没有例外,只是完全停止了活动。

    映射 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方法。
    已输入decodegzipstream方法。

    再也看不到了,所以电话后的线路 GZIPIN流 从未执行构造函数。

    有人知道为什么要这样包装 InputStream 从A BodySubscriber<InputStream> 在一个 GZIPIN流 是绞刑吗?

    注意:用于未编码(原始文本)HTTP响应主体的等效方法只包含对 BodySubscribers.ofInputStream() 无需映射,这就可以毫无问题地接收和显示响应。

    2 回复  |  直到 5 年前
        1
  •  2
  •   Bobulous    5 年前

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

    围绕一个

    不要使用 BodySubscribers.mapping -但是改变 InputStream 变成一个 GZIPInputStream 拿到HTTPResponse的尸体后:

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

    解决两个问题

    使映射函数返回 Supplier<InputStream> 相反,注意不要创建 GZIPIN流 直到 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;
        }
    }
    
        2
  •  1
  •   JGrassini    5 年前

    遇到了完全相同的问题。我在JavaDoc的 BodySubscribers.mapping 方法。同样的行为,应用程序挂起时没有任何错误。

    可能是一个bug,因为这是来自javadoc的官方示例。

      public static <W> BodySubscriber<W> asJSON(Class<W> targetType) {
         BodySubscriber<InputStream> upstream = BodySubscribers.ofInputStream();
    
         BodySubscriber<W> downstream = BodySubscribers.mapping(
               upstream,
               (InputStream is) -> {
                   try (InputStream stream = is) {
                       ObjectMapper objectMapper = new ObjectMapper();
                       return objectMapper.readValue(stream, targetType);
                   } catch (IOException e) {
                       throw new UncheckedIOException(e);
                   }
               });
        return downstream;
     } }