Filters in Coyote

来源:互联网 发布:java 虚拟机设置代理 编辑:程序博客网 时间:2024/05/24 01:10

不知道Filter是哪位牛人的创意,反正现在的编程框架中到处可见filter的身影,例如struts2、servlet等等。就连tomcat内部的实现,同样也少不了filter

接口:InputFilter OutputFilter

InputFilter OutputFilter都是继承自InputBuffer的接口,两个接口大同小异,下面以InputFilter为例,介绍主要的方法

  • public int doRead(ByteChunk chunk, Request unused):从chunk中读取数据,保存到filter内部。至于那个Request,很明显是作废了的
  • public void setRequest(Request request):设置request,这是因为某些filter需要request中额外的信息
  • public void setBuffer(InputBuffer buffer):设置下一个filter。因此coyote的filter和其他编程框架的类似,都是以pipeline的形式存在的,一个接一个

setup the filter

如何把这些filter装配起来呢?这一节涉及到的内容与 org.apache.coyote.http11.Http11Processor和InternalInputBuffer 有关

首先,在Http11Processor的prepareRequest()方法中,有关于装配filter的代码:

addInputFilter(inputFilters, encodingName);

而在这个方法中,又有如下代码:

if (encodingName.equals("identity")) {
            // Skip
        } else if (encodingName.equals("chunked")) {
            inputBuffer.addActiveFilter
                (inputFilters[Constants.CHUNKED_FILTER]);
            contentDelimitation = true;
        } else {
            for (int i = 2; i < inputFilters.length; i++) {
                if (inputFilters[i].getEncodingName()
                    .toString().equals(encodingName)) {
                    inputBuffer.addActiveFilter(inputFilters[i]);
                    return true;
                }
            }
            return false;
        }

 

核心代码还是在InternalInputBuffer 类的addActiveFilter方法中,这里才是真正“安装”filter

public void addActiveFilter(InputFilter filter) {

        if (lastActiveFilter == -1) {
            filter.setBuffer(inputStreamInputBuffer);
        } else {
            for (int i = 0; i <= lastActiveFilter; i++) {
                if (activeFilters[i] == filter)
                    return;
            }
            filter.setBuffer(activeFilters[lastActiveFilter]);
        }

        activeFilters[++lastActiveFilter] = filter;

        filter.setRequest(request);

    }

在上面的代码中,调用了刚才说到的filter.setBuffer方法,相当于把新加进来的fitler指向activeFilters的最后一个,可见activeFilters相当于一个堆栈,数组第一个元素相当于堆栈的底部

org.apache.coyote.http11.filters.ChunkedInputFilter

前面已经描述了filter是怎么组装起来的,现在以ChunkedInputFilter为例,主要分析一下InputBuffer.doRead这个很关键的接口方法。

这个filter的作用是解析http字节流中,被chunked了的报文内容(具体的可以参考http规范,据称是http1.1针对长连接的一种传输格式,也可参考我后来发的博客 http://blog.csdn.net/wangchengsi/archive/2009/03/22/4013821.aspx)

filter.setBuffer方法的参数是InputBuffer,故filter要取得http字节流,必定只能通过InputBuffer唯一的一个方法doRead来实现。过程大致如下:

前一个filter通过调用ChunkedInputFilter.doRead方法,可以从中获得所需的数据,而在ChunkedInputFilter.doRead中,ChunkedInputFilter本身又会调用自己前面的那个filter的doRead方法,如此递归下去直到最开头的filter(也就是activeFilters数组的第一个元素)

为了更加清楚地说明整个filter链路的处理过程,假设某个InternalInputBuffer有如下的filter链:

socket  JIOEndpoint   filter1 <- filter2 <- ..... <- filterN

JIOEndpoint 把socket交给coyote后,经过若干工序,字节流放入 InternalInputBuffer 的 filter1中;然后,调用 InputBuffer的doRead (因为coyote中几乎所有类都是InputBuffer),该doRead调用层层递归,到达 filterN,然后再沿着filter链递归至 filter1,完成数据的“过滤”。最后生成的Request才交给最外围的容器

附带 InternalInputBuffer 的doRead,从这里开始递归调用 filter

public int doRead(ByteChunk chunk, Request req)
        throws IOException {

        if (lastActiveFilter == -1)
            return inputStreamInputBuffer.doRead(chunk, req);
        else
           return activeFilters[lastActiveFilter].doRead(chunk,req);

    }

原创粉丝点击