Lighttpd1.4.20源码分析之插件系统(1)---plugin结构体和插件接口
在lighttpd中,使用插件的形式来增加服务的功能。同时,lighttpd提供了一个插件的公共接口给开发者,方便第三方提供额外的插件。Lighttpd的插件接口主要提供在plugin.h文件中。其中,plugin结构体是最核心的部分。
plugin结构体的定义如下:
1 typedef struct
2 {
3 size_t version;
4
5 buffer *name; /* name of the plugin */
6
7 void *(*init) ();
8 handler_t(*set_defaults) (server * srv, void *p_d);
9 handler_t(*cleanup) (server * srv, void *p_d);
10
11 /*
12 * is called ... 纯虚函数,在子类中要予以赋值。
13 */
14 handler_t(*handle_trigger) (server * srv, void *p_d); /* once a second */
15 handler_t(*handle_sighup) (server * srv, void *p_d); /* at a signup */
16 handler_t(*handle_uri_raw) (server * srv, connection * con, void *p_d); /* after uri_raw is set */
17 handler_t(*handle_uri_clean) (server * srv, connection * con, void *p_d);/* after uri is set */
18 handler_t(*handle_docroot) (server * srv, connection * con, void *p_d); /* getting the document-root */
19 handler_t(*handle_physical) (server * srv, connection * con, void *p_d); /* mapping url to physical path */
20 handler_t(*handle_request_done) (server * srv, connection * con, void *p_d); /* at the end of a request */
21 handler_t(*handle_connection_close) (server * srv, connection * con, void *p_d); /* at the end of a connection */
22 handler_t(*handle_joblist) (server * srv, connection * con, void *p_d); /* after all events are handled */
23 handler_t(*handle_subrequest_start) (server * srv, connection * con, void *p_d);
24
25 /*
26 * when a handler for the request has to be found
27 */
28 handler_t(*handle_subrequest) (server * srv, connection * con, void *p_d); /* */
29 handler_t(*connection_reset) (server * srv, connection * con, void *p_d); /* */
30 void *data;
31
32 /*
33 * dlopen handle
34 */
35 void *lib;
36 } plugin;
2 {
3 size_t version;
4
5 buffer *name; /* name of the plugin */
6
7 void *(*init) ();
8 handler_t(*set_defaults) (server * srv, void *p_d);
9 handler_t(*cleanup) (server * srv, void *p_d);
10
11 /*
12 * is called ... 纯虚函数,在子类中要予以赋值。
13 */
14 handler_t(*handle_trigger) (server * srv, void *p_d); /* once a second */
15 handler_t(*handle_sighup) (server * srv, void *p_d); /* at a signup */
16 handler_t(*handle_uri_raw) (server * srv, connection * con, void *p_d); /* after uri_raw is set */
17 handler_t(*handle_uri_clean) (server * srv, connection * con, void *p_d);/* after uri is set */
18 handler_t(*handle_docroot) (server * srv, connection * con, void *p_d); /* getting the document-root */
19 handler_t(*handle_physical) (server * srv, connection * con, void *p_d); /* mapping url to physical path */
20 handler_t(*handle_request_done) (server * srv, connection * con, void *p_d); /* at the end of a request */
21 handler_t(*handle_connection_close) (server * srv, connection * con, void *p_d); /* at the end of a connection */
22 handler_t(*handle_joblist) (server * srv, connection * con, void *p_d); /* after all events are handled */
23 handler_t(*handle_subrequest_start) (server * srv, connection * con, void *p_d);
24
25 /*
26 * when a handler for the request has to be found
27 */
28 handler_t(*handle_subrequest) (server * srv, connection * con, void *p_d); /* */
29 handler_t(*connection_reset) (server * srv, connection * con, void *p_d); /* */
30 void *data;
31
32 /*
33 * dlopen handle
34 */
35 void *lib;
36 } plugin;
可以看出,在结构体plugin的设计中,作者使用了面向对象的思想。plugin结构体就是一个虚基类,其中的数据成员,如name,version等都是子类公有的。而随后的一系列函数指针则是虚函数,这些函数指针在plugin结构体中并没有进行赋值,要求所有的子类必须对其进行赋值。不同的子类对这些函数指针赋不同的值,在进行函数调用的时候就可以实现多态。
另外,c语言毕竟不支持面向对象,因此,在通过c实现面向对象的时候大多情况先是要靠人的理解,而不是语言上的约束。如,这里说plugin结构体是一个虚基类,实际上所有的子类都是这个结构体的实例,而子类的实例只有一个,也就是他自己。这就和C++中的子类不同了。
在plugin结构体中,version成员比较重要。很明显,这个成员标记这个插件的版本。在plugin结构体中定义的那一系列函数指针是插件的对外接口,也就是插件对lighttpd的接口,lighttpd只知道这些接口,通过调用这些接口来完成工作。随着lighttpd的不断改进,这些接口可能满足不了服务器的要求,因此要对其进行改进,这样就有可能造成以前开发的插件无法使用。通过version成员,在加载插件的时候判断这个插件是否符合当前服务器的版本,也就是接口是否相符。如果不相符,则不加载插件,这样就可以避免由于接口的不相符造成服务器的崩溃等问题。
这些函数指针在lighttpd的文档中被称作'hooks'。分为serverwide hooks和connectionwide hooks,serverwide hooks是有服务器调用的,主要处理一些初始化等辅助的工作,包括:init,cleanup, set_defaults, handle_trigger和handle_sighup。connectionwide hooks主要是面向连接的,在处理连接的时候调用这些hooks完成相应的工作。这些hooks大部分在函数http_response_prepare()中被调用。
至于这些hooks是在哪被调用,都完成哪些功能,在后面分析具体的插件的时候会详细介绍。有兴趣的读者可以阅读lighttpd源码包中doc文件夹下的plugins文件。
在plugin.h中,plugin结构体的定义后面还有一堆的函数声明:
1 int plugins_load(server * srv);
2 void plugins_free(server * srv);
2 void plugins_free(server * srv);
这两个很明显是加载和释放插件函数。
1 handler_t plugins_call_handle_uri_raw(server * srv, connection * con);
2 handler_t plugins_call_handle_uri_clean(server * srv, connection * con);
3 handler_t plugins_call_handle_subrequest_start(server * srv, connection * con);
4 handler_t plugins_call_handle_subrequest(server * srv, connection * con);
5 handler_t plugins_call_handle_request_done(server * srv, connection * con);
6 handler_t plugins_call_handle_docroot(server * srv, connection * con);
7 handler_t plugins_call_handle_physical(server * srv, connection * con);
8 handler_t plugins_call_handle_connection_close(server * srv, connection * con);
9 handler_t plugins_call_handle_joblist(server * srv, connection * con);
10 handler_t plugins_call_connection_reset(server * srv, connection * con);
11
12 handler_t plugins_call_handle_trigger(server * srv);
13 handler_t plugins_call_handle_sighup(server * srv);
14
15 handler_t plugins_call_init(server * srv);
16 handler_t plugins_call_set_defaults(server * srv);
17 handler_t plugins_call_cleanup(server * srv);
2 handler_t plugins_call_handle_uri_clean(server * srv, connection * con);
3 handler_t plugins_call_handle_subrequest_start(server * srv, connection * con);
4 handler_t plugins_call_handle_subrequest(server * srv, connection * con);
5 handler_t plugins_call_handle_request_done(server * srv, connection * con);
6 handler_t plugins_call_handle_docroot(server * srv, connection * con);
7 handler_t plugins_call_handle_physical(server * srv, connection * con);
8 handler_t plugins_call_handle_connection_close(server * srv, connection * con);
9 handler_t plugins_call_handle_joblist(server * srv, connection * con);
10 handler_t plugins_call_connection_reset(server * srv, connection * con);
11
12 handler_t plugins_call_handle_trigger(server * srv);
13 handler_t plugins_call_handle_sighup(server * srv);
14
15 handler_t plugins_call_init(server * srv);
16 handler_t plugins_call_set_defaults(server * srv);
17 handler_t plugins_call_cleanup(server * srv);
这一系列的plugins_call_XXXXX函数则是插件对外的接口。也就是说,lighttpd服务器通过这些函数,调用插件进行工作。lighttpd在调用插件的时候并不知道到底调用的是哪些插件,而仅仅调用上面的函数。这些函数再调用相应的插件的函数,从而完成工作。具体怎么调用插件的函数,放在后面的文章中介绍。
最后面的config_XXXXXX函数是处理一些配置问题,暂不讨论。
在plugin.h文件中还定义了一些宏:
1 #define SERVER_FUNC(x) \
2 static handler_t x(server *srv, void *p_d)
3 #define CONNECTION_FUNC(x) \
4 static handler_t x(server *srv, connection *con, void *p_d)
5 #define INIT_FUNC(x) static void *x()
6
7 #define FREE_FUNC SERVER_FUNC
8 #define TRIGGER_FUNC SERVER_FUNC
9 #define SETDEFAULTS_FUNC SERVER_FUNC
10 #define SIGHUP_FUNC SERVER_FUNC
11 #define SUBREQUEST_FUNC CONNECTION_FUNC
12 #define JOBLIST_FUNC CONNECTION_FUNC
13 #define PHYSICALPATH_FUNC CONNECTION_FUNC
14 #define REQUESTDONE_FUNC CONNECTION_FUNC
15 #define URIHANDLER_FUNC CONNECTION_FUNC
2 static handler_t x(server *srv, void *p_d)
3 #define CONNECTION_FUNC(x) \
4 static handler_t x(server *srv, connection *con, void *p_d)
5 #define INIT_FUNC(x) static void *x()
6
7 #define FREE_FUNC SERVER_FUNC
8 #define TRIGGER_FUNC SERVER_FUNC
9 #define SETDEFAULTS_FUNC SERVER_FUNC
10 #define SIGHUP_FUNC SERVER_FUNC
11 #define SUBREQUEST_FUNC CONNECTION_FUNC
12 #define JOBLIST_FUNC CONNECTION_FUNC
13 #define PHYSICALPATH_FUNC CONNECTION_FUNC
14 #define REQUESTDONE_FUNC CONNECTION_FUNC
15 #define URIHANDLER_FUNC CONNECTION_FUNC
前面的三个宏(SERVER_FUNC, CONNECTION_FUNC和INIT_FUNC)定义了函数签名的模板。后面的一系列宏和plugin结构体中的函数指针对应,确定这些函数指针所对应的函数签名。
在进行插件开发的时候,插件中的函数签名要使用上面的宏来生成。这样可以保证接口的统一。
最后,还要提一下plugin.c文件中的结构体:
1 typedef struct
2 {
3 PLUGIN_DATA;
4 } plugin_data;
2 {
3 PLUGIN_DATA;
4 } plugin_data;
PLUGIN_DATA是一个宏,定义为:#define PLUGIN_DATA size_t id。
这个结构体用来存放插件所需要使用的数据,这个结构体作为plugin结构体中函数指针的最后一个参数:void *p_d传入对应的函数中。在plugin.c结构体中plugin_data的定义很简单,仅仅包含一个数据成员id。在mod_*.c/h文件中,同样也包含有plugin_data结构体的定义。如:mod_cgi.c中,
1 typedef struct {
2 PLUGIN_DATA;
3 buffer_pid_t cgi_pid;
4 buffer *tmp_buf;
5 buffer *parse_response;
6 plugin_config **config_storage;
7 plugin_config conf;
8 } plugin_data;
2 PLUGIN_DATA;
3 buffer_pid_t cgi_pid;
4 buffer *tmp_buf;
5 buffer *parse_response;
6 plugin_config **config_storage;
7 plugin_config conf;
8 } plugin_data;
在mod_cml.h中:
1 typedef struct {
2 PLUGIN_DATA;
3 buffer *basedir;
4 buffer *baseurl;
5 buffer *trigger_handler;
6 plugin_config **config_storage;
7 plugin_config conf;
8 } plugin_data;
2 PLUGIN_DATA;
3 buffer *basedir;
4 buffer *baseurl;
5 buffer *trigger_handler;
6 plugin_config **config_storage;
7 plugin_config conf;
8 } plugin_data;
等等。
这些定义有一个共通的特点,那就是第一个成员都是PLUGIN_DATA。这又是一个技巧。所有这些plugin_data相当于是plugin.c中plugin_data的子类。这些子类开始的部分和父类相同,这就允许子类的指针转换成父类指针,然后再转换回去,并保证数据不会丢失。这样,lighttpd所面对的插件数据接口是plugin.c中定义的plugin_data,当lighttpd在调用插件中的函数,并把数据传进去的时候,插件可以再把数据的类型还原回去。这样,对于lighttpd,所面对的数据接口就只有一个,插件所需要的数据可以不对lighttpd公开,这就很好的隐藏了数据。同时也简化了lighttpd的复杂度,提高了程序的扩展性。
下一篇中,将解释lighttpd中插件的加载和初始化。
分类:
Lighttpd源码分析
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述