nginx源码分析——filter模块

原创
2016/10/28 11:11
阅读数 904

1. filter模块简介

在nginx中,明确将HTTP响应分为两个部分——HTTP头部和HTTP包体,而filter模块的主要作用就是对HTTP响应信息进行加工处理。filter模块在NGX_HTTP_CONTENT_PHASE阶段参与处理(HTTP多阶段处理可参考这里),并且是在HTTP请求处理完毕后,才对HTTP头部和HTTP包体进行加工处理。有的filter模块仅对HTTP头部进行加工处理,有的仅对HTTP包体进行处理,也有的同时对HTTP头部和HTTP包体进行处理。另外,每个http请求都会被任意多个filter模块进行处理。也就是说,filter模块的处理效果是叠加的。例如,通过ngx_http_gzip_filter_module进行压缩处理后,再通过ngx_http_chunked_filter_module将响应包体以chunked编码形式发送。

2. filter模块

1). filter模块链表

通过调用ngx_http_send_header和ngx_http_output_filter发送HTTP头部、HTTP包体,最终会依次调用各个filter模块的处理函数完成对HTTP头部和HTTP包体的处理。

多个filter模块组成一个链表协同进行工作,而链表的实现则是通过四个函数指针来完成的。

其中ngx_http_top_header_filter和ngx_http_top_body_filter为全局变量,即ngx_http_send_header、ngx_http_output_filter只会调用这两个全局变量。

typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r);
typedef ngx_int_t (*ngx_http_output_body_filter_pt)(ngx_http_request_t *r, ngx_chain_t * chain);

ngx_http_output_header_filter_pt  ngx_http_top_header_filter;
ngx_http_output_body_filter_pt    ngx_http_top_body_filter;

ngx_int_t  ngx_http_send_header( ngx_http_request_t * r )
{
    if( r->post_action ) {
        return NGX_OK;
    }

    if( r->header_sent ) {
        ngx_log_error( NGX_LOG_ALERT, r->connection->log, 0, "header already sent" );
        return NGX_ERROR;
    }

    if( r->err_status ) {
        r->headers_out.status = r->err_status;
        r->headers_out.status_line.len = 0;
    }

    return ngx_http_top_header_filter(r);
}

ngx_int_t  ngx_http_output_filter( ngx_http_request_t * r, ngx_chain_t * in )
{
    ngx_int_t  rc;
    ngx_connection_t * c;

    c = r->connection;

    ngx_log_debug2( NGX_LOG_DEBUG_HTTP, c->log, 0, "http output filter \"%V?%V\"", &r->uri, &r->args );

    rc = ngx_http_top_body_filter(r, in);
    if( rc == NGX_ERROR ) {
        c->error = 1;
    }

    return rc;
}

ngx_http_next_header_filter和ngx_http_next_body_filter则是每个filter模块内部的静态变量,用于指向下一个filter模块的处理函数。通常是在http模块的postconfiguration处理中进行初始化。这样就形成了一个链表。

// ngx_http_chunked_filter_module.c
static  ngx_http_output_header_filter_pt   ngx_http_next_header_filter;
static  ngx_http_output_top_filter_pt      ngx_http_next_body_filter;

static  ngx_int_t  ngx_http_chunked_filter_init( ngx_conf_t * cf )
{
    ngx_http_next_header_filter = ngx_http_top_header_filter;
    ngx_http_top_headr_filter = ngx_http_chunked_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;
    ngx_http_top_body_filter = ngx_http_chunked_body_filter;

    return NGX_OK;
}

// ngx_http_gzip_filter_module.c
static  ngx_http_output_header_filter_pt   ngx_http_next_header_filter;
static  ngx_http_output_top_filter_pt      ngx_http_next_body_filter;

static  ngx_int_t  ngx_http_gzip_filter_init( ngx_conf_t * cf )
{
    ngx_http_next_header_filter = ngx_http_top_header_filter;
    ngx_http_top_headr_filter = ngx_http_chunked_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;
    ngx_http_top_body_filter = ngx_http_chunked_body_filter;

    return NGX_OK;
}

每个filter模块处理完毕后,通过调用ngx_http_next_header_filter或者ngx_http_next_body_filter,交由下一个filter模块进行处理。

static ngx_int_t  ngx_http_chunked_header_filter(ngx_http_request_t * r)
{
    ngx_http_core_loc_conf_t * clcf;
    ngx_http_chunked_filter_ctx_t * ctx;

    if( r->headers_out.status == NGX_HTTP_NOT_MODIFIED
        || r->headers_out.status == NGX_HTTP_NO_CONTENT
        || r->headers_out.status < NGX_HTTP_OK
        || r != r->main
        || r->method == NGX_HTTP_HEAD )
    {
        return ngx_http_next_header_filter(r);
    }

    if( r->headers_out.content_length_n == -1 ) {
        if( r->http_version < NGX_HTTP_VERSION_11 ) {
            r->keepalive = 0;

        } else {
            clcf = ngx_http_get_module_loc_conf( r, ngx_http_core_module );
            if( clcf->chunked_transfer_encoding ) {
                r->chunked = 1;
                ctx = ngx_pcalloc( r->pool, sizeof(ngx_http_chunked_filter_ctx_t) );
                if( ctx == NULL ) {
                    return NGX_ERROR;
                }
                ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module);

            } else {
                r->keepalive = 0;
            }
        }
    }
    return ngx_http_next_header_filter(r);
}

2). filter链表的顺序

前面提到了,通过指针将filter模块串联成链表,那么链表中各个模块的顺序是怎样决定的呢?

在make编译前执行configure命令时,会生成ngx_module.c文件,该文件中的ngx_modules数组保存了所有的nginx模块,当然也包括filter模块。nginx就是按照ngx_modules数组中成员的顺序来初始化filter模块链表的顺序的。下图为编译后ngx_module.c文件中ngx_modules数组的内容:

ngx_module_t * ngx_modules[] = {
    &ngx_core_module,
    &ngx_errlog_module,
    &ngx_conf_module,
    &ngx_regex_module,
    &ngx_events_module,
    &ngx_event_core_module,
    &ngx_epoll_module,
    &ngx_http_module,
    &ngx_http_core_module,
    &ngx_http_log_module,
    &ngx_http_upstream_module,
    &ngx_http_static_module,
    &ngx_http_autoindex_module,
    &ngx_http_index_module,
    &ngx_http_auth_basic_module,
    &ngx_http_access_module,
    &ngx_http_limit_conn_module,
    &ngx_http_limit_req_module,
    &ngx_http_geo_module,
    &ngx_http_map_module,
    &ngx_http_split_clients_module,
    &ngx_http_referer_module,
    &ngx_http_rewrite_module,
    &ngx_http_proxy_module,
    &ngx_http_fastcgi_module,
    &ngx_http_uwsgi_module,
    &ngx_http_scgi_module,
    &ngx_http_memcached_module,
    &ngx_http_empty_gif_module,
    &ngx_http_browser_module,
    &ngx_http_upstream_hash_module,
    &ngx_http_upstream_ip_hash_module,
    &ngx_http_upstream_least_conn_module,
    &ngx_http_upstream_keepalive_module,
    &ngx_http_upstream_zone_module,
    &ngx_http_write_filter_module,
    &ngx_http_header_filter_module,
    &ngx_http_chunked_filter_module,
    &ngx_http_range_header_filter_module,
    &ngx_http_gzip_filter_module,
    &ngx_http_postpone_filter_module,
    &ngx_http_ssi_filter_module,
    &ngx_http_charset_filter_module,
    &ngx_http_userid_filter_module,
    &ngx_http_headers_filter_module,
    &ngx_http_copy_filter_module,
    &ngx_http_range_body_filter_module,
    &ngx_http_not_modified_filter_module,
    NULL
};

需要注意的是:链表实际的顺序和ngx_modules数组中的顺序是相反的,即ngx_modules数组中靠前的模块,在链表中是靠后(调用处理)的。当然,你也可以在configure命令执行后,make命令执行前,自行修改ngx_modules.c文件的内容,对ngx_modules数组中的成员进行顺序上的调整。

3. 默认filter模块介绍

  • ngx_http_not_modified_filter_module

仅对HTTP头部做处理。在返回200成功时,根据请求中If-Modified-Since或者If-Unmodified-Since头部取得浏览器缓存文件的时间,再分析返回用户文件的最后修改事件,以此决定是否直接饭是钢304响应给用户。

  • ngx_http_range_body_filter_module

处理请求中的Range信息,根据Range中的要求返回文件的一部分给用户。

  • ngx_http_copy_filter_module

仅对HTTP包体做处理。将用户发送的ngx_chain_t结构的HTTP包体复制到新的ngx_chain_t结构中(都是各种指针的复制,不包括实际HTTP响应内容),后续的HTTP过滤模块处理的ngx_chain_t类型的成员都是ngx_http_copy_filter_module模块处理后的变量。

  • ngx_http_headers_filter_module

仅对HTTP头部做处理,允许通过修改nginx.conf配置文件,在返回给用户的响应中添加任意的HTTP头部。

  • ngx_http_userid_filter_module

仅对HTTP头部做处理。它基于cookie提供了简单的认证管理功能。

  • ngx_http_charset_filter_module

可以将文本返回给用户的响应包,按照nginx.conf中的配置重新进行编码,再返回给用户。

  • ngx_http_ssi_filter_module

支持SSI(Server Side Include,服务器端嵌入)功能,将文件内容包含到网页中并返回给用户。

  • ngx_http_postpone_filter_module

仅对HTTP包体做处理。它仅应用于subrequest产生的子请求。使得多个子请求同时向客户端发送响应时能够有序,所谓的"有序"是指按照构造子请求的顺序发送响应。

  • ngx_http_gzip_filter_module

对特定的HTTP响应包体(如网页或者文本文件)进行gzip压缩,再把压缩后的内容返回给用户。

  • ngx_http_range_header_filter_module

支持range协议。

  • ngx_http_chunked_filter_module

支持chunked编码

  • ngx_http_header_filter_module

仅对HTTP头部做处理。该模块会把r->headers_out结构体中的成员序列化为返回给用户的HTTP响应字符流,包括响应行和响应头部,并通过调用ngx_http_write_filter_module模块中的方法直接将HTTP头部发送到客户端。

  • ngx_http_write_filter_module

仅对HTTP包体做处理。该模块负责向客户端发送HTTP响应。

---------------------------------------------------------------------------------------------------------------------------------------------

参考:

《深入理解nginx》

展开阅读全文
加载中

作者的其它热门文章

打赏
0
1 收藏
分享
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部