为什么JDK11的HttpClient无法读取全文字符串?

问题描述 投票:0回答:1

在我的应用程序中,我使用 JDK11 HttpClient 向服务器发送 http 请求。
服务器将响应加密为十六进制字符串。
客户端代码可能如下所示:

HttpClient httpClient = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_1_1)
    .connectTimeout(Duration.ofSeconds(1))
    .build();
HttpRequest request = HttpRequest.newBuilder()
    .uri(new URI("xxx"))
    .POST(HttpRequest.BodyPublishers.ofString(encryptBody));
    .headers(xxx)
    .build();
HttpResponse<String> response = httpClient.send(request, HttpRequest.BodyHandlers.ofString());
// here returned body is just first part.
System.out.println("body : " + response.body());

例子: 服务器返回:

dfb3132c37f32cb93c78760749a6d9b0024e1 b0c5aa1dcd0bb1f02d611b72f0f5a073059475e659dd7aca7aad48e50f151e0ac215ab35eb8e041e569d779bd1a359007e0269dce3f3968ae070514d5ffd1591dc8b1b3e643c72888c9f95f9021071c3d8b7c8e540cc54df4762cb6a2086facac86e17cf023879936f725dfda5c60d97097785e2a62885cf6342a0d616ad632d7769518cc68720dd1d4bc9e20560e831a3eb6140747ae1f35e7fa136303b0df2fd7820fb2d1d1e467

但这里的响应体是:
dfb3132c37f32cb93c78760749a6d9b0024e1b0c5aa1dcd0bb1f02d611b72f0f5a073059475e659dd7aca7aad48e50f151e0ac215ab35eb8e041e569d779bd1a359007e0269dce3f3968ae070514d5f

我尝试使用 OkHttpClient 来发出请求。它运作良好。
所以,这是一个客户端问题。但我想不通。
任何回应将不胜感激。


OkHttpClient 代码运行良好,代码可能如下所示:

final MediaType JSON = MediaType.get("application/json; charset=utf-8");
RequestBody body = RequestBody.create(JSON, encryptBody);
Request req = new Request.Builder()
    .post(body)
    .url(xxx)
    .headers(xxx)
    .build();
Call call = client.newCall(req);
Response rep = call.execute();
System.out.println("body : " + rep.body.string());

服务器使用netty。核心处理程序如下:

class HttpResponseEncipher extends MessageToMessageEncoder<Netty4HttpResponse> {
    private static final String PROPERTY_DO_NOT_SPLIT = "es.unsafe.do_not_split_http_responses";

    private final boolean isSpiltResponse;

    private final int splitThreshold;

    HttpResponseEncipher() {
        isSpiltResponse = Booleans.parseBoolean(System.getProperty(PROPERTY_DO_NOT_SPLIT), false);
        splitThreshold = (int) (NettyAllocator.suggestedMaxAllocationSize() * 0.99);
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, Netty4HttpResponse msg, List<Object> out) {
        ByteBuf buf = msg.content();
        FullHttpResponse rep = msg.replace(encryptContent(buf));
        if (isSpiltResponse || rep.content().readableBytes() <= splitThreshold) {
            out.add(rep);
        } else {
            out.add(createPart(rep));
            ByteBuf content = rep.content();
            while (content.readableBytes() > splitThreshold) {
                out.add(new DefaultHttpContent(content.readRetainedSlice(splitThreshold)));
            }
            out.add(new DefaultLastHttpContent(content.readRetainedSlice(content.readableBytes())));
        }
    }

    private HttpResponse createPart(FullHttpResponse rep) {
        return new DefaultHttpResponse(rep.protocolVersion(), rep.status(), rep.headers());
    }
    
    private ByteBuf encryptContent(ByteBuf source) {
        int len = source.readableBytes();
        byte[] body = new byte[len];
        source.readBytes(body, 0, len);
        byte[] encryptedBody = AuthorityManager.getXXX().encrypt(body);
        String hexString = Hex.encodeHexString(encryptedBody);
        return Unpooled.wrappedBuffer(hexString.getBytes(StandardCharsets.UTF_8));
    }
}

在此之上,还有

HttpContentCompressor
HttpResponseEncoder
ReadTimeoutHandler

java http hex java-11
1个回答
0
投票

重写内容后重新计算内容长度。

protected void encode(ChannelHandlerContext ctx, Netty4HttpResponse msg, List<Object> out) {
    ByteBuf buf = msg.content();
    ByteBuf encryptBuf = encryptContent(buf);
    FullHttpResponse rep = msg.replace(encryptBuf);
    rep.headers().set("content-length", encryptBuf.readableBytes()); // recalc content length
    rep.headers().set("Content-Type", "text/plain");  // rewrite content type
    ...
}
© www.soinside.com 2019 - 2024. All rights reserved.