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
相关产品推荐
相关产品推荐

