Netty Server的HttpContentDecompressor移除请求头,能否配置保留?
解决Netty HttpContentDecompressor解压后保留原始Content-Encoding头的问题
默认情况下,io.netty.handler.codec.http.HttpContentDecompressor在完成请求解压后会移除content-encoding头——这符合HTTP规范,因为此时请求体已经是解压后的明文,该头原本的语义已经失效。但如果你的业务需要判断原始请求是否为压缩格式,可以通过以下两种方式实现需求:
方法一:自定义Decompressor保存原始头到自定义HTTP头
继承HttpContentDecompressor,重写decode方法,在父类处理前先把原始的content-encoding值复制到一个自定义头(比如X-Original-Content-Encoding)中,这样即使原头被移除,你依然能通过自定义头获取原始编码信息。
public class PreservingHttpContentDecompressor extends HttpContentDecompressor { @Override protected Object decode(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpRequest) { FullHttpRequest request = (FullHttpRequest) msg; String originalEncoding = request.headers().get(HttpHeaderNames.CONTENT_ENCODING); if (originalEncoding != null) { // 添加自定义头存储原始编码 request.headers().set("X-Original-Content-Encoding", originalEncoding); } } // 调用父类逻辑,父类会自动移除原content-encoding头 return super.decode(ctx, msg); } }
然后在Spring Boot的Netty服务器配置中,替换默认的HttpContentDecompressor为你自定义的实现:
@Configuration public class NettyServerConfig { @Bean public NettyServerCustomizer nettyServerCustomizer() { return httpServer -> httpServer.tcpConfiguration(tcpServer -> tcpServer.bootstrap(serverBootstrap -> serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // 遍历流水线找到默认的 decompressor 并替换 for (Map.Entry<String, ChannelHandler> entry : pipeline) { if (entry.getValue() instanceof HttpContentDecompressor) { pipeline.replace(entry.getKey(), "preserving-decompressor", new PreservingHttpContentDecompressor()); break; } } } }) ) ); } }
方法二:在Decompressor前添加Handler保存原始头到请求属性
如果不想修改Decompressor的逻辑,可以在流水线中HttpContentDecompressor之前添加一个自定义的ChannelInboundHandlerAdapter,把原始的content-encoding值存入请求的属性中,后续业务逻辑可以直接从属性中读取。
public class ContentEncodingPreserverHandler extends ChannelInboundHandlerAdapter { // 定义属性键,用于存储原始编码 private static final AttributeKey<String> ORIGINAL_CONTENT_ENCODING = AttributeKey.valueOf("ORIGINAL_CONTENT_ENCODING"); @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpRequest) { FullHttpRequest request = (FullHttpRequest) msg; String encoding = request.headers().get(HttpHeaderNames.CONTENT_ENCODING); if (encoding != null) { request.attr(ORIGINAL_CONTENT_ENCODING).set(encoding); } } super.channelRead(ctx, msg); } // 提供静态方法方便后续获取原始编码 public static String getOriginalEncoding(FullHttpRequest request) { return request.attr(ORIGINAL_CONTENT_ENCODING).get(); } }
然后在Netty配置中把这个Handler加到Decompressor前面:
@Configuration public class NettyServerConfig { @Bean public NettyServerCustomizer nettyServerCustomizer() { return httpServer -> httpServer.tcpConfiguration(tcpServer -> tcpServer.bootstrap(serverBootstrap -> serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // 在默认的 decompressor 前添加自定义Handler pipeline.addBefore("decompressor", "content-encoding-preserver", new ContentEncodingPreserverHandler()); } }) ) ); } }
后续在业务逻辑中,就可以通过ContentEncodingPreserverHandler.getOriginalEncoding(request)获取原始的压缩编码信息了。
内容的提问来源于stack exchange,提问作者V John
相关产品推荐
相关产品推荐

