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

Netty 4.1 WebSocket代理场景:ProxyHandler未聚合分块HTTP响应的解决方法

解决Netty 4.1 ProxyHandler处理分块代理响应引发的NotSslRecordException问题

我之前在搭建经代理的WebSocket连接时也碰到过一模一样的问题,核心原因就是默认的ProxyHandler在收到代理服务器的响应头后,就直接把后续的分块HTTP内容传递给了后面的SslHandler,而SslHandler会把这些HTTP分块当成SSL握手记录来解析,自然就抛出NotSslRecordException了。下面给你两种可行的解决方案:

方案一:在Pipeline中提前添加HttpObjectAggregator

这是最简单直接的方法——在ProxyHandler之前加入HttpObjectAggregator,让它先把所有分块的HTTP响应聚合成一个完整的FullHttpResponse,再交给ProxyHandler处理。这样ProxyHandler就能等到整个代理响应接收完成后,再触发后续的SslHandler逻辑,避免把HTTP分块内容误传给SSL处理器。

示例Pipeline配置:

ChannelPipeline pipeline = ch.pipeline();

// 1. 编解码HTTP请求/响应
pipeline.addLast(new HttpClientCodec());
// 2. 聚合分块HTTP响应,设置足够大的容量(这里设为1MB)
pipeline.addLast(new HttpObjectAggregator(1024 * 1024));
// 3. 处理代理连接逻辑
pipeline.addLast(new ProxyHandler(new InetSocketAddress(proxyHost, proxyPort)));
// 4. SSL处理器(仅在代理握手完成后才会处理真正的SSL流量)
pipeline.addLast(new SslHandler(sslEngine));
// 5. WebSocket客户端协议处理器
pipeline.addLast(new WebSocketClientProtocolHandler(
    WebSocketClientHandshakerFactory.newHandshaker(
        new URI(wsUrl), WebSocketVersion.V13, null, true, new DefaultHttpHeaders()
    )
));
// 自定义业务handler
pipeline.addLast(new MyWebSocketClientHandler());

方案二:自定义ProxyHandler子类处理分块响应

如果你需要更精细的控制(比如不想全局聚合所有HTTP请求/响应),可以继承ProxyHandler,重写channelRead方法,自己收集并聚合分块的HTTP内容,直到收到LastHttpContent后再交给父类处理。

示例代码:

public class AggregatingProxyHandler extends ProxyHandler {
    private HttpResponse currentResponse;
    private ByteBuf aggregatedContent;

    public AggregatingProxyHandler(InetSocketAddress proxyAddress) {
        super(proxyAddress);
        aggregatedContent = Unpooled.buffer();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof HttpResponse) {
            // 保存响应头,初始化聚合内容
            currentResponse = (HttpResponse) msg;
            aggregatedContent.clear();
        } else if (msg instanceof HttpContent) {
            HttpContent content = (HttpContent) msg;
            // 累加分块内容
            aggregatedContent.writeBytes(content.content());
            content.release(); // 释放原内容的内存

            if (msg instanceof LastHttpContent) {
                // 收到最后一块,把聚合后的内容设置到响应中
                currentResponse.content().writeBytes(aggregatedContent);
                // 交给父类处理完整的响应
                super.channelRead(ctx, currentResponse);
                aggregatedContent.clear();
            }
        } else {
            // 非HTTP对象直接传递
            super.channelRead(ctx, msg);
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 释放缓存的ByteBuf,避免内存泄漏
        aggregatedContent.release();
        super.channelInactive(ctx);
    }
}

使用这个自定义handler替换原来的ProxyHandler即可,它会确保只有完整的代理响应被处理后,后续的SslHandler才会收到数据。

关键注意点

  • 确保HttpObjectAggregator的容量设置足够大,能容纳代理服务器返回的最大响应内容,避免因内容过大抛出异常。
  • 如果代理服务器返回的是101 Switching Protocols响应(WebSocket升级用的响应),两种方案都能正确处理聚合,因为FullHttpResponse兼容101响应的格式。

内容的提问来源于stack exchange,提问作者Tomasz Kryński

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:26:32