lighttpd 之 十 一 插件链

1 概述

     在Lighttpd应用程序中灵活的插件结构使用户可以轻易地修改服务器功能而不用改变Lighttpd的核心代码。多个插件的同时存在使Lighttpd应用程序必须以一种合理的方式组织和调用它们,以便用户可以简单地增加或减少加载的插件。本章内容就将以Lighttpd中最简单的mod_access插件为例子解析各个插件的共同结构以及插件与插件之间的组织方式。

      本节相关部分源码:
      base.h
      plugin.h
      plugin.c
      mod_access.c
      mod_skeleton.c

2 插件内部结构

     虽然每个插件完成的功能各有不同,但是为了统一组织和调用,使各个插件都有统一的数据结构和函数接口。这种固定的模式既便于对已有插件的统一管理又利于用户开发自己特定的功能插件。源文件mod_skeleton.c就是一个完整的插件骨架,用户完全可以利用它来开发自定义插件。为了简单清晰地讲解插件结构,下面我们以mod_access插件为例子进行分析。

2.1 数据结构

      Lighttpd利用结构体plugin(如清单9-1所示)来组织一个插件,可以看到该结构体内绝大部分的字段都是函数指针,一共15个,当然对于一个插件来说,也许它并不会提供所有的回调函数地址,因此该插件对应的plugin中就会有某些函数指针字段指向NULL。plugin内另外一个值得关注的字段为data字段,它将用来保存(说“指向”也许更准确,不过在本书里都没有做明确区分,希望读者能灵活理解)插件的相关数据,这在本章后面的代码分析中可以清楚地看到这一点。

      在最简单的情况下,插件需要的数据结构只要可以存储其相应的配置信息就足够了,如果某插件需要存储除配置信息以外的数据,则在结构体plugin_data内增加相应的记录字段即可,这是相当灵活的,在后面的具体插件分析章节中,我们可以看到这一点。

       清单9-1 plugin数据结构定义

//mod_access.c
1.typedef struct{
/*存储对应的"url.access-deny"配置信息值,因为可以有多个,所以这里需要设置为array类型。*/
2.array*access_deny;
3.}plugin_config;
4.typedef struct{
/*PLUGIN_DATA宏定义见下面,可以认为是插件的id编号,在后面章节还可以看到该编号用于标记(con->mode)客户端连接是由Lighttpd自己处理还是由其他特定插件处理。*/
5.PLUGIN_DATA;
/*和7.3.2节的srv->config_storage类似,用于存储解析后的配置信息值,当然这里存储的是仅与插件mod_access有关的配置信息值,即"url.access-deny"。*/
6.plugin_config**config_storage;
7.plugin_config conf;
8.}plugin_data;
//plugin.h
9.#define SERVER_FUNC(x)\
static handler_t x(server*srv,void*p_d)
10.#define CONNECTION_FUNC(x)\
static handler_t x(server*srv,connection*con,void*p_d)
11.#define INIT_FUNC(x)\
static void*x()
12.#define FREE_FUNC SERVER_FUNC
13.#define TRIGGER_FUNC SERVER_FUNC
14.#define SETDEFAULTS_FUNC SERVER_FUNC
15.#define SIGHUP_FUNC SERVER_FUNC
16.#define SUBREQUEST_FUNC
CONNECTION_FUNC
17.#define JOBLIST_FUNC CONNECTION_FUNC
18.#define PHYSICALPATH_FUNC CONNECTION_FUNC
19.#define REQUESTDONE_FUNC CONNECTION_FUNC
20.#define URIHANDLER_FUNC CONNECTION_FUNC
21.#define PLUGIN_DATA size_t id
22.typedef struct{
23.size_t version;/*插件版本号。*/
24.buffer*name;/*name of the plugin*//*插件名称。*/
25./*15个函数回调接口。*/
26.void*(*init)();
27.handler_t(*set_defaults)(server*srv,void*p_d);
28.handler_t(*cleanup)(server*srv,void*p_d);
29./*is called...*/
30.handler_t(*handle_trigger)(server*srv,void*p_d);
31./*once a second*/
32.handler_t(*handle_sighup)(server*srv,void*p_d);
33./*at a signup*/
34.handler_t(*handle_uri_raw)(server*srv,connection*con,void*p_d);
35./*after uri_raw is set*/
36.handler_t(*handle_uri_clean)(server*srv,connection*con,void*p_d);
37./*after uri is set*/
38.handler_t(*handle_docroot)(server*srv,connection*con,void*p_d);
39./*getting the document-root*/
40.handler_t(*handle_physical)(server*srv,connection*con,void*p_d);
41./*mapping url to physical path*/
42.handler_t(*handle_request_done)(server*srv,connection*con,void*p_d);
43./*at the end of a request*/
44.handler_t(*handle_connection_close)(server*srv,connection*con,void*p_d);
45./*at the end of a connection*/
46.handler_t(*handle_joblist)(server*srv,connection*con,void*p_d);
47./*after all events are handled*/
48.handler_t(*handle_subrequest_start)(server*srv,connection*con,void*p_d);
49./*when a handler for the request
50.*has to be found
51.*/
52.handler_t(*handle_subrequest)(server*srv,connection*con,void*p_d);
53./**/
54.handler_t(*connection_reset)(server*srv,connection*con,void*p_d);
55./**/
56.void*data;/*指向plugin_data。*/
57./*dlopen handle*/
58.void*lib;
59.}plugin;
//plugin.c
60.typedef enum{
61.PLUGIN_FUNC_UNSET,
62.PLUGIN_FUNC_HANDLE_URI_CLEAN,
63.PLUGIN_FUNC_HANDLE_URI_RAW,
64.PLUGIN_FUNC_HANDLE_REQUEST_DONE,
65.PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE,
66.PLUGIN_FUNC_HANDLE_TRIGGER,
67.PLUGIN_FUNC_HANDLE_SIGHUP,
68.PLUGIN_FUNC_HANDLE_SUBREQUEST,
69.PLUGIN_FUNC_HANDLE_SUBREQUEST_START,
70.PLUGIN_FUNC_HANDLE_JOBLIST,
71.PLUGIN_FUNC_HANDLE_DOCROOT,
72.PLUGIN_FUNC_HANDLE_PHYSICAL,
73.PLUGIN_FUNC_CONNECTION_RESET,
74.PLUGIN_FUNC_INIT,
75.PLUGIN_FUNC_CLEANUP,
76.PLUGIN_FUNC_SET_DEFAULTS,
77.
78.PLUGIN_FUNC_SIZEOF
79.}plugin_t;

      上面提到的结构体plugin_data内存储的信息在整个插件生命周期内基本都是不变的,如字段config_storage内存储的配置信息等。为了针对不同客户端请求连接而存储不同的插件环境信息(即这些数据的生命周期仅为一次请求连接),Lighttpd在结构体connection内提供了一个void类型的数组指针(void**plugin_ctx;),该数组即可存储各插件需要的连接环境信息(一般是一个名为handler_ctx的结构体),存储位置通过插件id编号来标记,比如某插件id编号为1,那么与它对应的连接环境信息(如果存在)则为con->plugin_ctx[p->id](其中con为代表某次连接请求的connection变量,plugin_data为该插件的数据结构)。这些连接环境信息会随着连接请求的完成而失效,因此在整个插件生命周期内是不断地根据客户的请求而变化的。

2.2 函数接口

       Lighttpd中每个插件可以拥有最多16个接口函数,这些函数将在某些状态或事件下触发而被调用以完成某一特定功能,按照触发状态或事件的来源可以将它们分为两类,一类为由于Lighttpd服务器自身的事件发生而触发;另一类为由于客户端请求连接的处理事件而触发。表9-1给出了这16个接口函数的名称、触发条件以及功能和部分返回值;图9-1显示了这些函数执行的大致前后关系。了解它们的这种前后关系,对于我们在后面分析具体插件模块代码时有好处.

        

扫描二维码关注公众号,回复: 3523885 查看本文章

图 9-1 插件接口函数执行大致流程

       对于各回调函数返回的枚举值 HANDLER_GO_ON 和 HANDLER_ERROR 定义在头文件 settings.h 内,可以看到枚举取值并不只有这两个。在多数情况下的请求处理回调函数只会返回 HANDLER_GO_ON、HANDLER_ERROR 和 HANDLER_FINISHED 这三个枚举值中的一个。在多数情况下的请求处理回调函数只会返回 HANDLER_GO_ON、HANDLER_ERROR 和 HANDLER_FINISHED 这三个枚举值中的一个。从后面的分析内容我们可以知道,对于客户端的请求,插件是串联处理的,当串链中的某一个插件处理完后,如果需要后面的插件继续处理该请求,此时我们通常需要返回 HANDLER_GO_ON 枚举值。枚举值 HANDLER_ERROR 表示发生了致命的错误使我们不得不终止当前请求连接甚至关闭 Lighttpd 应用程序。枚举值 HANDLER_FINISHED 则表示对于完成当前连接请求的所有工作都已经准备就绪,这通常有两个意思,要么是一个非 200 的状态码被设置,要么是完成该请求的所需内容已经生成,只需发送出去即可。对于枚举值 HANDLER_WAIT_FOR_EVENT 和 HANDLER_WAIT_FOR_FD 则表示插件没有处理完成并需要等待下一次 fd-event 事件。这两个值通常用在异步事件处理的插件中,如 mod_proxy、mod_fastcgi 等。当需要重新检查请求结构(request-structur)时(如在 mod_rewrite 插件中重写 URI),则需要返回 HANDLER_COMEBACK 状态码。

清单 9-2 handler_t 数据结构定义

//settings.h
typedef enum{HANDLER_UNSET,
HANDLER_GO_ON,
HANDLER_FINISHED,
HANDLER_COMEBACK,
HANDLER_WAIT_FOR_EVENT,
HANDLER_ERROR,
HANDLER_WAIT_FOR_FD
}handler_t;

      对于每个插件如何具体地实现这些函数,这里并不打算做进一步的分析,将留在后面的具体插件分析章节中各个讲解,下面开始进入多个插件的组织结构相关方面的解析。

3 插件组织结构

3.1 串链结构

      Lighttpd 中插件的组织结构初看起来十分简单,即利用一个 plugin 数组保存所有插件(上一小节已经讲过,每个插件对应一个 plugin),但是每个插件所拥有的接口函数各不相同,因此仅仅这样简单的串联插件 plugin 是不够的,还需要将各个插件的同类接口函数也串联起来,这样才能在需要进行事件处理时快速顺利地依次调用各个插件的同类接口函数。

      例如,Lighttpd 加载了四个插件分别为:plugin-1、plugin-2、plugin-3、plugin-4,它们各自拥有的接口函数如图 9-2 所示,一共有三类接口函数(handle_uri_clean、handle_trigger、handle_uri_raw),因此将形成四条串链(在物理存储上,实际是利用一个数组,这里说“链”是为了更形象,读者应注意区分和理解),一条为所有插件的串联,另外三条为按分类(三类)支持同类接口函数插件的串联。当然,这只是最直白的文字表述,具体内容参见下面的源码分析。

     

     图 9-2 插件接口串联

3.2 插件组织结构源码分析

        Lighttpd 加载插件对应的结构体 plugin 保存在 server 结构体的 plugins 字段内,这段相关代码如清单 9-3 所示。
       清单 9-3 plugin 数据结构相关函数

//plugin.c
80.static plugin*plugin_init(void){
81.plugin*p;
82.p=calloc(1,sizeof(*p));
83.return p;
84.}
85.
86.static void plugin_free(plugin*p){
87.int use_dlclose=1;
88.if(p->name)buffer_free(p->name);/*插件名称内存空间释放。*/
89.#ifdef HAVE_VALGRIND_VALGRIND_H
90./*if(RUNNING_ON_VALGRIND)use_dlclose=0;*/
91.#endif
92.#ifndef LIGHTTPD_STATIC
93.if(use_dlclose&&p->lib){
94.#ifdef__WIN32
95.FreeLibrary(p->lib);
96.#else
/*利用共享库(shared library)来实现插件(Plug-in)功能是十分常见的做法,UNIX/Linux 里提供了 4 个库函数(dlopen、dlerror、dlsym 和 dlclose),一个头文件(dlfcn.h)以及两个共享库(静态库 libdl.a 和动态库 libdl.so),以支持所谓的动态链接装入器。关于这方面的更多信息,读者可以查阅 MAN 手册(man ld.so 或 man dlopen 等)。*/
/*关闭句柄并取消共享目标文件的映射。
//base.h
typedef struct{
void*ptr;
size_t used;
size_t size;
}buffer_plugin;
*/
97.dlclose(p->lib);
98.#endif
99.}
100.#endif
101.free(p);/*结构体内存空间释放。*/
102.}
103.
104.static int plugins_register(server*srv,plugin*p){
105.plugin**ps;
/*这种申请内存(即一次申请多个结构体所需内存,尽量避免反复的申请释放,减少内存碎片的产生)的做法在 Lighttpd 源码里的很多地方都可以看到。*/
106.if(0==srv->plugins.size){
107.srv->plugins.size=4;
108.srv->plugins.ptr=malloc(srv->plugins.size*sizeof(*ps));
109.srv->plugins.used=0;
110.}else if(srv->plugins.used==srv->plugins.size){
111.srv->plugins.size+=4;
112.srv->plugins.ptr=realloc(srv->plugins.ptr,srv->plugins.size*sizeof(*ps));
113.}
114.ps=srv->plugins.ptr;
115.ps[srv->plugins.used++]=p;/*加入插件链。*/
116.return 0;
117.}
118.void plugins_free(server*srv){
119.size_t i;
120.plugins_call_cleanup(srv);/*调用各模块自身的清理函数。*/
121.for(i=0;i<srv->plugins.used;i++){
122.plugin*p=((plugin**)srv->plugins.ptr)[i];
123.plugin_free(p);
124.}
125.for(i=0;srv->plugin_slots&&i<PLUGIN_FUNC_SIZEOF;i++){
126.plugin**slot=((plugin***)(srv->plugin_slots))[i];
127.if(slot)free(slot);
128.}
129.free(srv->plugin_slots);
130.srv->plugin_slots=NULL;
131.free(srv->plugins.ptr);
132.srv->plugins.ptr=NULL;
133.srv->plugins.used=0;
134.}    

       插件加载函数 plugins_load()定义了两次(由于宏的选择作用,只有一个会被编译到 Lighttpd 二进制程序中),分别用于完成插件的两种不同加载方式(即静态加载和动态加载)。
清单 9-4 函数 plugins_load

//plugin.c
135.#ifdef LIGHTTPD_STATIC
/*静态加载方式 72。*/
136.int plugins_load(server*srv){
137.plugin*p;
/*这个宏包含多条 C 语句。*/
138.#define PLUGIN_INIT(x)\
p=plugin_init();\
if(x##_plugin_init(p)){\
log_error_write(srv,__FILE__,__LINE__,"ss",#x,"plugin init failed");\
plugin_free(p);\
return-1;\
}\
plugins_register(srv,p);
/*头文件 plugin-static.h 在 Lighttpd 源文件包内无法找到,它是在选用静态编译时自动生成,包含如下这样的宏定义,这些宏(代码 138 行)展开后刚好就是进行插件的加载。
//……省略……
PLUGIN_INIT(mod_access)
PLUGIN_INIT(mod_dirlisting)
PLUGIN_INIT(mod_status)
//……省略……
*/
139.#include"plugin-static.h"
140.return 0;
141.}
142.#else
143.int plugins_load(server*srv){/*动态加载方式。*/
144.plugin*p;
145.int(*init)(plugin*pl);
146.const char*error;
147.size_t i;
148.
149.for(i=0;i<srv->srvconf.modules->used;i++){
/*代码 150~159 行用于构建插件的模块文件路径。*/
150.data_string*d=(data_string*)srv->srvconf.modules->data[i];
151.char*modules=d->value->ptr;
152.buffer_copy_string_buffer(srv->tmp_buf,srv->srvconf.modules_dir);
153.buffer_append_string_len(srv->tmp_buf,CONST_STR_LEN("/"));
154.buffer_append_string(srv->tmp_buf,modules);
155.#if defined(__WIN32)||defined(__CYGWIN__)
156.buffer_append_string_len(srv->tmp_buf,CONST_STR_LEN(".dll"));
157.#else
158.buffer_append_string_len(srv->tmp_buf,CONST_STR_LEN(".so"));
159.#endif
160.p=plugin_init();
161.#ifdef__WIN32
162.//……省略……
163.#else
/*载入并映射插件动态模块库。*/
164.if(NULL==(p->lib=dlopen(srv->tmp_buf->ptr,
RTLD_NOW|RTLD_GLOBAL))){
165.log_error_write(srv,__FILE__,__LINE__,"sbs","dlopen()failed for:",
srv->tmp_buf,dlerror());
166.plugin_free(p);
167.return-1;
168.}
169.#endif
/*代码 170~172 行用于构建各插件的初始化函数"*_plugin_init"的字符串符号。*/
170.buffer_reset(srv->tmp_buf);
171.buffer_copy_string(srv->tmp_buf,modules);
172.buffer_append_string_len(srv->tmp_buf,CONST_STR_LEN("_plugin_init"));
173.#ifdef__WIN32
174.//……省略……
175.#else
176.#if 1/*获取初始化函数的函数地址。*/
177.init=(int(*)(plugin*))(intptr_t)dlsym(p->lib,srv->tmp_buf->ptr);
178.#else
179.*(void**)(&init)=dlsym(p->lib,srv->tmp_buf->ptr);
180.#endif
181.if((error=dlerror())!=NULL){/*检查是否发生错误。*/
182.log_error_write(srv,__FILE__,__LINE__,"s",error);
183.plugin_free(p);
184.return-1;
185.}
186.#endif
187.if((*init)(p)){/*成功加载完一个插件,立即调用其初始化函数。*/
188.log_error_write(srv,__FILE__,__LINE__,"ss",modules,
"plugin init failed");
189.plugin_free(p);
190.return-1;
191.}
192.#if 0
193.log_error_write(srv,__FILE__,__LINE__,"ss",modules,"plugin loaded");
194.#endif
195.plugins_register(srv,p);/*注册,即加入到插件链。*/
196.}
197.return 0;
198.}
199.#endif

       在所有的插件模块成功加载并在 srv->plugins 上成功注册串联后,Lighttpd 就要进行另外一项重要工作,即将各个插件的同类接口函数也串联起来,这部分代码实现在函数 plugins_call_init()内,该函数完成的接口函数串联任务在整个 Lighttpd 生命周期内只需进行一次,因此该函数也就只被 Lighttpd 调用执行一次。
清单 9-5 函数 plugins_call_init

   //plugin.c
200./**
201.*-call init function of all plugins to init the plugin-internals
202.*-added each plugin that supports has callback to the corresponding slot
203.*-is only called once.
204.*/
205.handler_t plugins_call_init(server*srv){
206.size_t i;
207.plugin**ps;
208.ps=srv->plugins.ptr;/*所有串联模块的存储地址。*/
209./*fill slots*/
/*PLUGIN_FUNC_SIZEOF 为枚举结构 plugin_t 的最后一个元素,它表示接口函数分类个数,因此分配的串链存储空间最多 PLUGIN_FUNC_SIZEOF 个。*/
210.srv->plugin_slots=calloc(PLUGIN_FUNC_SIZEOF,sizeof(ps));
/*所有加载插件逐个处理。*/
211.for(i=0;i<srv->plugins.used;i++){
212.size_t j;
213./*check which calls are supported*/
214.plugin*p=ps[i];
215.#define PLUGIN_TO_SLOT(x,y)\
/*p 插件提供有 y 接口函数。*/
if(p->y){\
/*y 接口函数对应的函数链。*/
plugin**slot=((plugin***)(srv->plugin_slots))[x];\
/*函数链存储空间不存在则先申请。*/
if(!slot){\
slot=calloc(srv->plugins.used,sizeof(*slot));\
((plugin***)(srv->plugin_slots))[x]=slot;\
}\
/*将 p 插件提供的 y 接口函数加入到对应的函数链,该 for 循环从头到尾寻找该函数链中第一个未使用的存储空间,找到后存入并 break 跳出,完成此次接口函数链的加入。*/
for(j=0;j<srv->plugins.used;j++){\
if(slot[j])continue;\
slot[j]=p;\
break;\
}\
}
/*各类接口函数链的创建。*/
216.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN,
handle_uri_clean);
217.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW,handle_uri_raw);
218.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE,
handle_request_done);
219.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE,
handle_connection_close);
220.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER,handle_trigger);
221.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP,handle_sighup);
222.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST,
handle_subrequest);
223.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START,
handle_subrequest_start);
224.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST,handle_joblist);
225.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT,handle_docroot);
226.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL,handle_physical);
227.PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET,
connection_reset);
228.PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP,cleanup);
229.PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS,set_defaults);
230.#undef PLUGIN_TO_SLOT
/*插件提供了初始化函数则调用它以进行初始化工作,主要创建插件的数据存储空间。*/
231.if(p->init){
232.if(NULL==(p->data=p->init())){
233.log_error_write(srv,__FILE__,__LINE__,"sb",
234."plugin-init failed for module",p->name);
235.return HANDLER_ERROR;
236.}
/*设置插件 id 编号。*/
237./*used for con->mode,DIRECT==0,plugins above that*/
238.((plugin_data*)(p->data))->id=i+1;
/*检查插件版本号。即某版本的 Lighttpd 应该使用对应版本号的插件。*/
239.if(p->version!=LIGHTTPD_VERSION_ID){
240.log_error_write(srv,__FILE__,__LINE__,"sb",
"plugin-version doesn't match lighttpd-version for",p->name);
241.return HANDLER_ERROR;
242.}
243.}else{
244.p->data=NULL;
245.}
246.}
247.return HANDLER_GO_ON;
248.}

       各个插件同类接口函数的串联使得 Lighttpd 在利用已加载插件来处理某类事件时变得容易,即只需要顺序调用该类接口函数链上的回调函数便可,这个依次调用过程被封装在各个函数内,在头文件 plugin.h 内。我们可以看到这些函数的声明(如清单 9-6 所示)。由于这些函数的实现十分类似,因此在源文件 plugin.c 内,它们都被以宏的形式统一实现,这样做的最大好处就是在以后的代码维护修改过程中只需改动宏定义就可以做到对各个函数的统一修改。

       9.2.2 节曾经讲过,这些接口函数分为两类,因此这里的宏实现也有两个。
清单 9-6 公共接口函数声明

   //plugin.h
332.handler_t plugins_call_handle_uri_raw(server*srv,connection*con);
333.handler_t plugins_call_handle_uri_clean(server*srv,connection*con);
334.handler_t plugins_call_handle_subrequest_start(server*srv,connection*con);
335.handler_t plugins_call_handle_subrequest(server*srv,connection*con);
336.handler_t plugins_call_handle_request_done(server*srv,connection*con);
337.handler_t plugins_call_handle_docroot(server*srv,connection*con);
338.handler_t plugins_call_handle_physical(server*srv,connection*con);
339.handler_t plugins_call_handle_connection_close(server*srv,connection*con);
340.handler_t plugins_call_handle_joblist(server*srv,connection*con);
341.handler_t plugins_call_connection_reset(server*srv,connection*con);
342.
343.handler_t plugins_call_handle_trigger(server*srv);
344.handler_t plugins_call_handle_sighup(server*srv);
345.handler_t plugins_call_set_defaults(server*srv);
346.handler_t plugins_call_cleanup(server*srv);

    清单 9-7 中所示宏展开后就可得到头文件 plugin.h 内声明函数的定义体,这些函数在 Lighttpd 处理客户端连接请求时至关重要。
    清单 9-7 宏扩展得到插件公共接口函数

//plugin.c
249.#define PLUGIN_TO_SLOT(x,y)\
250.handler_t plugins_call_##y(server*srv,connection*con){\
251.plugin**slot;\
252.size_t j;\
/*不存在任何接口函数链,直接返回 HANDLER_GO_ON。*/
253.if(!srv->plugin_slots)return HANDLER_GO_ON;\
/*获取该类接口函数链。*/
254.slot=((plugin***)(srv->plugin_slots))[x];\
/*该类接口函数链不存在(即没有任何插件提供了该类接口函数),直接返回 HANDLER_GO_ON。*/
255.if(!slot)return HANDLER_GO_ON;\
/*依次顺序调用该类接口函数链上的接口函数。*/
256.for(j=0;j<srv->plugins.used&&slot[j];j++){\
257.plugin*p=slot[j];\
258.handler_t r;\
259.switch(r=p->y(srv,con,p->data)){\
/*调用的插件接口函数返回 HANDLER_GO_ON 时才继续进行下一个插件的处理,否则表示处理完毕(成功或失败或其他),函数返回。*/
260.case HANDLER_GO_ON:\
261.break;\
262.case HANDLER_FINISHED:\
263.case HANDLER_COMEBACK:\
264.case HANDLER_WAIT_FOR_EVENT:\
265.case HANDLER_WAIT_FOR_FD:\
266.case HANDLER_ERROR:\
267.return r;\
268.default:\
269.log_error_write(srv,__FILE__,__LINE__,"sbs",#x,p->name,
"unknown state");\
270.return HANDLER_ERROR;\
271.}\
272.}\
273.return HANDLER_GO_ON;\
274.}
275.
276./**
277.*plugins that use
278.*-server*srv
279.*-connection*con
280.*-void*p_d(plugin_data*)
281.*/
/*利用上面的宏来定义各个函数。*/
282.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN,handle_uri_clean)
283.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW,handle_uri_raw)
284.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE,
handle_request_done)
285.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE,
handle_connection_close)
286.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST,handle_subrequest)
287.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START,
handle_subrequest_start)
288.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST,handle_joblist)
289.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT,handle_docroot)
290.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL,handle_physical)
291.PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET,connection_reset)
/*取消宏定义。*/
292.#undef PLUGIN_TO_SLOT
/*重新定义,由于接口函数分为两类,因此宏也分为两类定义,它们仅在参数等方面略有不同。*/293.#define PLUGIN_TO_SLOT(x,y)\
294.handler_t plugins_call_##y(server*srv){\
295.plugin**slot;\
296.size_t j;\
297.if(!srv->plugin_slots)return HANDLER_GO_ON;\
298.slot=((plugin***)(srv->plugin_slots))[x];\
299.if(!slot)return HANDLER_GO_ON;\
300.for(j=0;j<srv->plugins.used&&slot[j];j++){\
301.plugin*p=slot[j];\
302.handler_t r;\
303.switch(r=p->y(srv,p->data)){\
304.case HANDLER_GO_ON:\
305.break;\
306.case HANDLER_FINISHED:\
307.case HANDLER_COMEBACK:\
308.case HANDLER_WAIT_FOR_EVENT:\
309.case HANDLER_WAIT_FOR_FD:\
310.case HANDLER_ERROR:\
311.return r;\
312.default:\
313.log_error_write(srv,__FILE__,__LINE__,"sbsd",#x,p->name,
"unknown state:",r);\
314.return HANDLER_ERROR;\
315.}\
316.}\
317.return HANDLER_GO_ON;\
318.}
319.
320./**
321.*plugins that use
322.*-server*srv
323.*-void*p_d(plugin_data*)
324.*/
325.
326.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER,handle_trigger)
327.PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP,handle_sighup)
328.PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP,cleanup)
329.PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS,set_defaults)
330.
331.#undef PLUGIN_TO_SLOT

4 本章总结

        本章从宏观上介绍了 Lighttpd 中的插件源码部分,这包括各模块内部的统一结构与各模块之间组织结构,理解这些内容对于我们后面章节的学习十分重要。本章并没有针对 Lighttpd 的特定插件功能进行分析,但阅读本章内容仍然可以帮助读者迅速正确地开发出自定义的功能插件模块。

猜你喜欢

转载自blog.csdn.net/caofengtao1314/article/details/82963611