HttpAggregator源码浅析

文章目录

HttpObjectAggregator源码

HttpObject类继承关系

MessageToMessageDecoder

MessageToMessageDecoder<I> 继承自 ChannelInboundHandlerAdapter,它用于将1种消息转为另外一类的消息。

acceptInboundMessage方法用于判断传过来的msg是否是指定的泛型,如果msg是指定的泛型,则交给decode(ChannelHandlerContext ctx, I msg, List<Object> out)方法去做解码,该解码方法由子类自行实现;如果不是指定的泛型,则直接添加到out中;

在channelRead方法的最后面,遍历out,遍历添加至out的消息,传递给后面的入站处理器。

java 复制代码
public abstract class MessageToMessageDecoder<I> extends ChannelInboundHandlerAdapter {

    private final TypeParameterMatcher matcher;

    protected MessageToMessageDecoder() {
        matcher = TypeParameterMatcher.find(this, MessageToMessageDecoder.class, "I");
    }

    protected MessageToMessageDecoder(Class<? extends I> inboundMessageType) {
        matcher = TypeParameterMatcher.get(inboundMessageType);
    }

    public boolean acceptInboundMessage(Object msg) throws Exception {
        return matcher.match(msg);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        
        CodecOutputList out = CodecOutputList.newInstance();
        
        try {
            
            // acceptInboundMessage方法用于判断传过来的msg是否是指定的泛型
            if (acceptInboundMessage(msg)) {
                
                I cast = (I) msg;
                try {
                    // 如果msg是指定的泛型,
                    // 则交给`decode(ChannelHandlerContext ctx, I msg, List<Object> out)`方法去做解码
                    // 该解码方法由子类自行实现
                    decode(ctx, cast, out);
                } finally {
                    ReferenceCountUtil.release(cast);
                }
            } else {
                out.add(msg);
            }
        } catch (DecoderException e) {
            throw e;
        } catch (Exception e) {
            throw new DecoderException(e);
        } finally {
            try {
                int size = out.size();
                // 遍历out,遍历添加至out的消息,传递给后面的入站处理器
                for (int i = 0; i < size; i++) {
                    ctx.fireChannelRead(out.getUnsafe(i));
                }
            } finally {
                out.recycle();
            }
        }
    }

    protected abstract void decode(ChannelHandlerContext ctx, I msg, List<Object> out);
}

MessageAggregator

先看类声明MessageAggregator<I, S, C extends ByteBufHolder, O extends ByteBufHolder> extends MessageToMessageDecoder<I>

I - 起始消息和内容消息 所属类型,如HttpObject;

S - 起始消息 所属类型,如 HttpMessage;

C - 内容消息 所属类型,如 HttpContent;

O - 完整消息 所属类型,如 FullHttpMessage;

属性

java 复制代码
// 消息内容最大长度
private final int maxContentLength;

// 当前正在处理的消息
private O currentMessage;

// 是否处理过长消息
private boolean handlingOversizedMessage;

// 最多允许的子ByteBuf的数量
private int maxCumulationBufferComponents = 1024;

// 当前handler对应的ChannelHandlerContext
private ChannelHandlerContext ctx;

private ChannelFutureListener continueResponseWriteListener;

// 当前是否正在聚合,初始化为false
private boolean aggregating;

构造方法

构造方法就是传入最大允许的内容长度

java 复制代码
protected MessageAggregator(int maxContentLength) {
    validateMaxContentLength(maxContentLength);
    this.maxContentLength = maxContentLength;
}

protected MessageAggregator(int maxContentLength, Class<? extends I> inboundMessageType) {
    super(inboundMessageType);
    validateMaxContentLength(maxContentLength);
    this.maxContentLength = maxContentLength;
}

acceptInboundMessage(Object msg)

根据前面MessageToMessageDecoder类中acceptInboundMessage方法中,当msg是指定类型时,交给子类的decode去作解码。子类MessageAggregator重写了acceptInboundMessage,如下:

java 复制代码
@Override
public boolean acceptInboundMessage(Object msg) throws Exception {

    // 首先调用父类的acceptInboundMessage方法,表示如果不是指定的泛型,直接返回false。
    // 返回false后,将直接添加到out中,然后传给后面的入站处理器,不会进入到后面的聚合操作。
    if (!super.acceptInboundMessage(msg)) {
        return false;
    }

    // 走到这里,说明msg是指定泛型类型的消息
    I in = (I) msg;

    // 这里的isAggregated是个抽象方法,表示in是否是已经聚合好了的消息
    // 比如子类HttpObjectAggregator判断in是FullHttpMessage类型,那就表示不需要聚合操作了,
    // 直接传给后面的入站处理器就行了
    if (isAggregated(in)) {
        return false;
    }

    // 这里的isStartMessage是个抽象方法,表示in是否是起始消息
    // 比如子类HttpObjectAggregator判断in是HttpMessage类型,那就表示要开始聚合消息了
    // 因为Http消息是先来1个包含消息头和协议版本的HttpMessage消息,后面再跟HttpContent内容的
    if (isStartMessage(in)) {
        aggregating = true;
        return true;
    }
    // 这里的isContentMessage是个抽象方法,表示in是否是内容消息
    // 比如子类HttpObjectAggregator判断in是HttpContent类型
    // 必须是先有了HttpMessage消息后,后续来HttpContent消息,后续的HttpContent才会交给decode方法去作聚合处理
    else if (aggregating && isContentMessage(in)) {
        return true;
    }

    // 其它情况一律不做decode,直接传给后面的入站处理器
    return false;
}

decode

java 复制代码
@Override
protected void decode(final ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception {
    
    // 表示必须是先有了HttpMessage之后, 进入到聚合,先进入HttpMessage,后续跟HttpContent消息
    assert aggregating;

    // 如果msg是起始消息,
    // 即msg是HttpMessage类型(协议版本和请求头)
    if (isStartMessage(msg)) {
        
        // 先初始化 正在处理过长消息 为 false
        handlingOversizedMessage = false;
        
        // 如果currentMessage不为null, 则释放原来聚合好的消息, 并将它置为null, 然后抛出异常
        if (currentMessage != null) {
            currentMessage.release();
            currentMessage = null;
            throw new MessageAggregationException();
        }

        S m = (S) msg;

        // 根据 HttpMessage 确定是否要写入1个 continueResponse, 
        // 如果要写入,则返回1个不为null的FullHttpResponse响应对象;如果不要写入,则返回null
        // 用于处理客户端请求发送Expect: 100-continue时的场景, 可能返回null, 413的FullHttpResponse, 417的FullHttpResponse, 100的FullHttpResponse
        Object continueResponse = newContinueResponse(m, maxContentLength, ctx.pipeline());
        
        // 需要写出 continueReponse(FullHttpResponse类型)
        if (continueResponse != null) {

            // 第一次处理到时, 才初始化listener, 并缓存下来这个 continueResponse写监听器
            ChannelFutureListener listener = continueResponseWriteListener;
            if (listener == null) {
                continueResponseWriteListener = listener = new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future)  {
                        if (!future.isSuccess()) {
                            // 如果写出 continueReponse 失败, 则触发异常事件传播
                            ctx.fireExceptionCaught(future.cause());
                        }
                    }
                };
            }

            // 调用子类方法, 判断 在写完 continueResponse 之后, 是否需要关闭
            boolean closeAfterWrite = closeAfterContinueResponse(continueResponse);
            
            // 调用子类方法, 返回是否忽略后面的内容, 标记handlingOversizedMessage
            // HttpObjectAggregator判断continueResponse如果是4xx响应状态码, 则标记handlingOversizedMessage为true
            handlingOversizedMessage = ignoreContentAfterContinueResponse(continueResponse);

            // 异步写出continueResponse
            // 并添加监听器, 当写出失败时, 传播异常事件
            final ChannelFuture future = ctx.writeAndFlush(continueResponse).addListener(listener);

            // 如果需要写完之后关闭channel, 则添加监听器, 在写操作完成之后, 执行关闭操作, 并结束当前方法 
            if (closeAfterWrite) {
                future.addListener(ChannelFutureListener.CLOSE);
                return;
            }
            
            // 如果 handlingOversizedMessage 为 true, 结束当前方法
            // (即返回给客户端的响应状态码是417/413)
            if (handlingOversizedMessage) {
                return;
            }
            
        } 
        // 如果 continueResponse 是null, 
        // 并且 content-length请求头存在且大于maxContentLength
        else if (isContentLengthInvalid(m, maxContentLength)) { 
            
			// 处理过长消息, 返回413, 看是否需要关闭连接
            invokeHandleOversizedMessage(ctx, m);
            
            // 结束当前方法
            return;
        }

        // 正常处理了expect:100-continue 或者 没有expect头并且消息没有过长
        
        // 如果请求头解码有错误, 也需要聚合起来传递给后面的入站处理器
        if (m instanceof DecoderResultProvider && !((DecoderResultProvider) m).decoderResult().isSuccess()) {
            
            // 聚合起来的对象
            O aggregated;
            
            // 如果 m 也实现了ByteBufHolder(一般不会走这个分支吧), 则把内容聚合起来
            if (m instanceof ByteBufHolder) {
                // 开始聚合(由子类实现)
                aggregated = beginAggregation(m, ((ByteBufHolder) m).content().retain());
            } else {
                // 如果 m 没实现ByteBufHolder, 则聚合1个空的内容,开始聚合(由子类实现)
                aggregated = beginAggregation(m, EMPTY_BUFFER);
            }
            
            // 完成聚合, 将aggregating置为false, 如果未设置content-length, 则会设置进去
            finishAggregation0(aggregated);
            
            // 传递给后面的入站处理
            out.add(aggregated);
            
            // 结束当前方法
            return;
        }

        // 说明前面解码正常
        
        // 初始化用来存储消息体内容的ByteBuf容器
        CompositeByteBuf content = ctx.alloc().compositeBuffer(maxCumulationBufferComponents);
        
        // 如果 m 实现了 ByteBufHolder 接口, 则将内容作组合到 消息体 中
        if (m instanceof ByteBufHolder) {
            appendPartialContent(content, ((ByteBufHolder) m).content());
        }
        
        // 开始聚合, 创建 AggregatedFullHttpRequest 或 AggregatedFullHttpResponse
        // 并初始化 currentMessage 属性, 它表示当前正在处理的消息, 后续的消息内容会组合到此消息中
        currentMessage = beginAggregation(m, content);
        
    } else if (isContentMessage(msg)) { // 如果msg实现了HttpContent接口
        
        // 如果 currentMessage 是null, 则忽略(丢弃), 可以结束当前方法, 直到下一次的请求或响应
        if (currentMessage == null) {
            return;
        }

		// 用于存储当前收到的消息内容
        CompositeByteBuf content = (CompositeByteBuf) currentMessage.content();

        // 当前的消息内容
        final C m = (C) msg;
        
		// 如果已经收到的消息内容 加上 当前收到的消息内容的 长度 超过了最大允许的内容长度
        if (content.readableBytes() > maxContentLength - m.content().readableBytes()) {
            S s = (S) currentMessage;
            // 处理过长消息, 返回413, 看是否需要关闭连接
            invokeHandleOversizedMessage(ctx, s);
            // 结束当前方法
            return;
        }

        // 将当前收到的消息内容组合到消息体中
        appendPartialContent(content, m.content());

        // 给子类1个机会去处理
        // 子类HttpObjectAggregator会判断m如果是LastHttpContent, 则会将trailingHeaders都设置给currentMessage
        aggregate(currentMessage, m);

        // 判断当前收到的消息内容是否是最后一块消息内容了
        final boolean last;
        
        // 如果当前收到的消息内容 实现了DecoderResultProvider
        if (m instanceof DecoderResultProvider) {
            
            // 先看解码结果是否是成功的, 
            DecoderResult decoderResult = ((DecoderResultProvider) m).decoderResult();
            
            // 如果解码不成功
            if (!decoderResult.isSuccess()) {
                // 把失败的原因记录给当前聚合的消息currentMessage
                if (currentMessage instanceof DecoderResultProvider) {
                    ((DecoderResultProvider) currentMessage).setDecoderResult(
                        DecoderResult.failure(decoderResult.cause()));
                }
                // 并标记是最后一块消息了, 因为已经失败了
                last = true;
            } else {
                // 如果解码成功, 则判断 当前收到的消息是否是LastHttpContent消息
                last = isLastContentMessage(m);
            }
        } else {
            // 判断 当前收到的消息是否是LastHttpContent消息
            last = isLastContentMessage(m);
        }

        // 如果是最后一块消息内容 或者 解码某块内容时错误
        if (last) {
           
            // 完成聚合, 将aggregating置为false, 如果未设置content-length, 则会设置进去
            finishAggregation0(currentMessage);

            // 将当前消息传给后续的入站处理器
            out.add(currentMessage);
            
            // 处理以上后, currentMessage重新置为null, 可以处理下一个请求或响应了
            currentMessage = null;
        }
    } else {
        throw new MessageAggregationException();
    }
}
newContinueResponse(S start, int maxContentLength, ChannelPipeline pipeline)

HttpObjectAggregator#newContinueResponse实现如下:

java 复制代码
@Override
protected Object newContinueResponse(HttpMessage start, int maxContentLength, ChannelPipeline pipeline) {
    
    // 处理起始消息的expect请求头
    Object response = continueResponse(start, maxContentLength, pipeline);
    
    // 如果处理了expect请求头,就移除掉expect请求头
    if (response != null) {
        start.headers().remove(EXPECT);
    }
    
    // 返回response 
    return response;
}

private static Object continueResponse(HttpMessage start, 
                                       int maxContentLength, 
                                       ChannelPipeline pipeline) {

    // 如果 起始消息 是 HttpRequest类型, 并且 协议版本是1.1及以上, 则返回false
    // 否则 expect请求头不为空 并且 该请求头的值 不是 continue, 则返回true, 其它情况返回false
    // (表示不支持expectation请求头的情况)
    if (HttpUtil.isUnsupportedExpectation(start)) {

        // 触发自定义事件传播 HttpExpectationFailedEvent.INSTANCE
        pipeline.fireUserEventTriggered(HttpExpectationFailedEvent.INSTANCE);

        // 返回 417 状态码(因为这个对象是公用的,所以每次用都要retained)
        return EXPECTATION_FAILED.retainedDuplicate();

    } else if (HttpUtil.is100ContinueExpected(start)) {
        // 如果 起始消息 是 HttpRequest类型, 并且 协议版本是1.1及以上
        // 并且 expect请求头 存在, 且值为 100-continue

        // 获取 content-length 请求头的值, 如果有的话, 并且它不超过maxContentLength, 
        // 则返回 100 状态码, 表示可以继续上传
        if (getContentLength(start, -1L) <= maxContentLength) {
            return CONTINUE.retainedDuplicate();
        }

        // 表示内容超过了maxContentLength允许的长度
        // 触发自定义事件传播 HttpExpectationFailedEvent.INSTANCE
        pipeline.fireUserEventTriggered(HttpExpectationFailedEvent.INSTANCE);

        // 返回 413 状态码
        return TOO_LARGE.retainedDuplicate();
    }

    return null;
}
closeAfterContinueResponse

HttpObjectAggregator#newContinueResponse实现如下:

java 复制代码
@Override
protected boolean closeAfterContinueResponse(Object msg) {
    // 如果配置的 closeOnExpectationFailed 为true(默认是false), 并且响应状态码是4xx, 则返回true
    // 其它情况, 则返回false
    return closeOnExpectationFailed && ignoreContentAfterContinueResponse(msg);
}
ignoreContentAfterContinueResponse

HttpObjectAggregator#ignoreContentAfterContinueResponse实现如下:

java 复制代码
@Override
protected boolean ignoreContentAfterContinueResponse(Object msg) {
    if (msg instanceof HttpResponse) {
        final HttpResponse httpResponse = (HttpResponse) msg;
        // 响应状态码是 4xx, 即客户端错误
        return httpResponse.status().codeClass().equals(HttpStatusClass.CLIENT_ERROR);
    }
    return false;
}
invokeHandleOversizedMessage

MessageAggregator#invokeHandleOversizedMessage实现如下

java 复制代码
private void invokeHandleOversizedMessage(ChannelHandlerContext ctx, S oversized) throws Exception {

    // 标记 handlingOversizedMessage 为true
    handlingOversizedMessage = true;

    // 当前正在聚合的消息 的引用 置为null
    currentMessage = null;

    try {

        // 处理过长消息
        handleOversizedMessage(ctx, oversized);

    } finally {

        // 释放 oversized
        ReferenceCountUtil.release(oversized);
    }
}

protected void handleOversizedMessage(ChannelHandlerContext ctx, 
                                      S oversized) throws Exception {
    // 默认实现 是 传播 TooLongFrameException异常事件
    ctx.fireExceptionCaught(
        new TooLongFrameException("content length exceeded " 
                                  + maxContentLength() + " bytes."));
}
handleOversizedMessage

HttpObjectAggregator#handleOversizedMessage 重写了该方法

java 复制代码
@Override
protected void handleOversizedMessage(final ChannelHandlerContext ctx, 
                                      HttpMessage oversized) throws Exception {
    // 如果是客户端发过来的请求太大了
    if (oversized instanceof HttpRequest) {
        
        // 如果过长消息是FullHttpMessage类型 或者 (那就是HttpRequest或HttpResponse咯)expect头不是100-contine的请求头情况并且connection头不是keep-alive
        if (oversized instanceof FullHttpMessage 
            || !HttpUtil.is100ContinueExpected(oversized) && !HttpUtil.isKeepAlive(oversized)
           ) {
            
            // 异步写给客户端1个413的响应状态码
            ChannelFuture future = ctx.writeAndFlush(TOO_LARGE_CLOSE.retainedDuplicate());
            
            // 写完413状态码后,关闭连接
            future.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (!future.isSuccess()) {
                        logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause());
                    }
                    ctx.close();
                }
            });
        } else {
            // 写完413状态码后,如果写入操作没有成功则关闭连接
            ctx.writeAndFlush(TOO_LARGE.retainedDuplicate()).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (!future.isSuccess()) {
                        logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause());
                        ctx.close();
                    }
                }
            });
        }
    } else if (oversized instanceof HttpResponse) { // 如果是服务端发过来的响应太大了
        
        // 关闭连接
        ctx.close();
        
        // 排除内容过长的异常
        throw new TooLongHttpContentException("Response entity too large: " + oversized);
        
    } else {
        // 其它类型的消息不支持
        throw new IllegalStateException();
    }
}
beginAggregation

HttpObjectAggregator#beginAggregation

java 复制代码
@Override
protected FullHttpMessage beginAggregation(HttpMessage start, ByteBuf content) throws Exception {
    
    // start消息一定不能是 FullHttpMessage,都已经完成了,还咋聚合
    assert !(start instanceof FullHttpMessage);

    // 这里传入false,表示移除掉start消息中的transfer-encoding消息头的 chunked值
    HttpUtil.setTransferEncodingChunked(start, false);

    AggregatedFullHttpMessage ret;
    
    if (start instanceof HttpRequest) {
        // 聚合, 传入start,content 和 trailingHeaders
        ret = new AggregatedFullHttpRequest((HttpRequest) start, content, null);
    } else if (start instanceof HttpResponse) {
        // 聚合, 传入start,content 和 trailingHeaders
        ret = new AggregatedFullHttpResponse((HttpResponse) start, content, null);
    } else {
        // 不支持其它类型
        throw new Error();
    }
    
    return ret;
}
相关推荐
9527出列3 天前
Netty实战--使用netty构建WebSocket服务
websocket·网络协议·netty
猫吻鱼18 天前
【Netty4核心原理⑯】【Netty高性能调优工具类解析】
netty
后端小张23 天前
【JAVA 进阶】深入探秘Netty之Reactor模型:从理论到实战
java·开发语言·网络·spring boot·spring·reactor·netty
❀͜͡傀儡师1 个月前
springboot集成mqtt服务,自主下发
java·spring boot·后端·mqtt·netty
onAcorner1 个月前
Netty/Redis网络模型——IO多路复用原理(操作系统)
netty·nio
tanxinji1 个月前
Netty编写Echo服务器
java·netty
fanly111 个月前
在抖音直播推广开源作品的可行性?
微服务·netty·.net core·microservice
9527出列1 个月前
Netty源码分析(终)--关于WriteAndFlush
netty·源码阅读
C2H5OH6661 个月前
Netty详解-02
java·websocket·网络协议·tcp/ip·tomcat·netty·nio