在许多关于Apache模块开发的资料中,关于Perl语言的介绍比比皆是,而C++的内容却寥寥无几。本文旨在填补这一空白,介绍如何使用C++构建Apache模块。 必须指出的是,无法完全避开C语言,因为Apache和APR都是用C语言编写的,也将使用C语言来与之接口。目标是将其封装起来,使应用程序可以完全用C++编写。
所有项目都需要一个起点。因此,让花些时间来搭建开发环境。首先,这些文章主要针对基于*nix的用户(如Linux、FreeBSD等)。如果是微软Windows用户,这里也有足够的信息让开始,但前提是已经是一个经验丰富的微软开发者。这里描述的一切都是基于基于CentOS6的开发系统。
在设置中,有一个运行CentOS6的专用服务器,也可以使用虚拟机。CentOS6服务器还安装了X-Window。 对于实际的开发,使用一台运行Microsoft Windows 7的笔记本电脑。安装了Putty和Xming,这让可以直接在Windows桌面上打开各种工具。然而,*nix的纯粹主义者也可以只使用一台机器,如果他们喜欢的话。
首先,需要确保服务器配置了所有需要的软件包。在CentOS6上,使用yum安装了以下软件包:
yum groupinstall 'Development Tools'
httpd-2.2.15-15.el6.centos.1.x86_64
httpd-devel-2.2.15-15.el6.centos.1.x86_64
httpd-tools-2.2.15-15.el6.centos.1.x86_64
注意 - 如果使用的是32位机器,请确保加载了正确的32位软件包。
接下来,从安装Netbeans用于C/C++,并将其安装到服务器上。可能还需要安装Java运行时包,因为Netbeans使用它。 如果在服务器上本地运行,现在应该能够打开Netbeans。然而,如果像一样在Microsoft Windows上,将想要安装Putty和Xming。设置Putty以允许X-Forwarding。一旦通过SSH连接到服务器,运行Netbeans应该会将IDE显示在Microsoft Windows桌面上的一个漂亮的Xming窗口中。
此时,可以创建一个标准的helloworld.c项目,只是为了测试工具链,并确保一切正常工作。
如果对Apache模块开发完全不熟悉,那么阅读标准的Apache helloworld教程是一个很好的开始。应该花几分钟熟悉这个教程,因为这正是将以C/C++格式实现的。记住,无法逃避C语言,但可以很好地将其隐藏在封装的对象中。所以,一旦看过Apache教程,就回到这里,将开始。
让从创建Netbeans项目开始。从文件 > 新建项目开始,选择C/C++,项目类型应该是C/C++动态库,如下图所示:
在下一个屏幕上,输入了“mod_foo”作为项目名称,而不是“helloworld”,因为将在未来的文章中使用这个项目。 所以现在有一个准备开始的裸项目。但首先需要设置一些项目偏好,然后构建一个合理的文件系统布局。首先是布局:
假设在自己的工作目录中,转到项目:
cd NetBeansProjects/mod_foo
mkdir src
mkdir include
布局应该看起来像这样:
现在在项目上右键单击,选择弹出上下文菜单底部的属性。选择“链接器”,然后选择“输出”。默认的Netbeans使用的是将输出称为“libmod_foo”,想将其更改为“mod_foo”,如下所示:
接下来选择C编译器,在包含目录中输入以下内容:
include [见注释1]
/usr/include/httpd
/usr/include/apr-l
/usr/local/include
/usr/include
[1]注意,这是作为“相对”包含添加的,选择上面创建的目录。其余的都是系统目录,应该在服务器上是标准的。这里有一张图表显示了这些添加:
到目前为止都很好,但请注意,到目前为止配置的是“Debug (active)”构建配置。当构建Release版本时,将不得不重复这个,并添加Release构建配置的属性。
观察敏锐的人会从图片中注意到,这样做的时候是以root用户登录的,而不是普通账户。主要原因是稍后会看到它确实使调试事情变得更容易。但这里有一个警告,使用开发系统上的root账户是一个安全风险。确保开发系统在防火墙后面,并且物理上也得到了很好的保护(是的,当在厕所的时候,老板颠倒了所有的屏幕,只是为了证明一个观点!)。
在开始编写代码之前,有一件事应该在Netbeans项目上做。将创建类对象,封装Apache和APR接口。为了构建项目并使其更清晰,将使用Netbeans的“逻辑”文件夹创建一个"Apache"目录。在这里,将放置所有与Apache交互的对象。在“源文件”和“头文件”中都这样做。还添加了应用程序的逻辑文件夹,以将纯C++应用程序代码与Apache封装代码分开。有关更多详细信息,请参见这些图片:
现在是时候创建前两个文件,mod_foo.cpp和mod_foo.hpp,它们应该在源文件和头文件的“根”文件夹中创建。确保指定了之前制作的文件夹(src或include):
最后应该有类似这样的东西:
现在可以开始构建Apache的“样板”代码,并添加一些其他东西,使C++链接工作得很好。
这里是这两个文件:
#ifndef MOD_FOO_HPP
#define MOD_FOO_HPP
#ifdef __cplusplus
#define EXTERN_C_BLOCK_BEGIN extern "C" {
#define EXTERN_C_BLOCK_END }
#define EXTERN_C_FUNC extern "C"
#else
#define EXTERN_C_BLOCK_BEGIN
#define EXTERN_C_BLOCK_END
#define EXTERN_C_FUNC
#endif
#include <httpd.h>
#include <http_protocol.h>
#include <http_config.h>
#endif /* MOD_FOO_HPP */
#include "mod_foo.hpp"
EXTERN_C_FUNC int foo_handler( request_rec* inpRequest )
{
int nReturnVal = DECLINED;
if (inpRequest->handler != NULL && strcmp(inpRequest->handler, "foo") == 0)
{
ap_rputs("Hello World from FOO", inpRequest);
nReturnVal = OK;
}
return nReturnVal;
}
EXTERN_C_FUNC void foo_hooks( apr_pool_t* inpPool )
{
ap_hook_handler( foo_handler, NULL, NULL, APR_HOOK_MIDDLE );
}
EXTERN_C_BLOCK_BEGIN
module AP_MODULE_DECLARE_DATA foo_module =
{
STANDARD20_MODULE_STUFF,
NULL,
NULL,
NULL,
NULL,
NULL,
foo_hooks
};
EXTERN_C_BLOCK_END
所以,在Netbeans中可以通过按F11来构建这个,如果一切顺利,将得到一个成功的构建。现在是时候安装模块并给它的第一个测试运行。
在ssh shell中改变目录到Netbeans构建模块的地方。在服务器上它在这里:~/NetBeansProjects/mod_foo/dist/Debug/GNU-Linux-x86/。在那里应该看到一个名为mod_foo.so的文件,可以将其安装到Apache中。注意,如果ssh shell不是root,将需要root权限来做到这一点:
# apxs –n foo –I mod_foo.so
Apache的apxs工具应该作为Apache安装的一部分被安装。对于标准的64位服务器,这将把mod_foo.so安装到/usr/lib64/httpd/modules/。
现在模块已经就位,需要激活它。在/etc/httpd/conf.d中创建一个名为foo.conf的新文件,并在其中放置:
LoadModule foo_module modules/mod_foo.so
<Location /foo>
SetHandler foo
</Location>
最后,要以单进程/非线程模式运行Apache,使用httpd –X调用,这将导致Apache运行直到用Ctrl-C杀死它,然后指向最喜欢的Firefox ,如果一切正常,应该看到"Hello World from FOO"打印出来。
在继续编写一些不错的OOPs代码之前,值得一提如何使用Netbeans中集成的gdb调试器。显然,不会尝试在Netbeans中构建整个Apache并从那里运行它(尽管可以)。
最简单的方法是使用Netbeans的“Attach to process”在Debug菜单下。在弹出对话框中输入"httpd –X"进行过滤,将看到从命令行启动的进程。选择它并附加。然后可以在foo_handler()函数中设置一个断点。当刷新Firefox时,它将在那个点中断。