13.10 创建自己的处理器
在了解了处理器的细节之后我们就可以创建自己的处理器了。本章中我们将创建一个简单的处理器example_handler,该处理器的作用很简单,只是返回固定的报文信息。
13.10.1 定义处理器
在创建自己的过滤器之前,你必须首先要考虑清楚你的处理器的名称以及它在什么条件下会被调用。对于我们的示例处理器而言,处理器的名称为”example_handler”,它的触发条件则如下所示:
<Location /example_status>
SetHandler example_handler
</Location>
example_handler在URI为http://xxx.xxx.xxx/example_status的时候被触发,为此我们必须在配置文件httpd.conf中增加上面的配置信息。
13.10.2 声明处理器
一旦确定处理的名称,那么我们就可以定义该处理器。事实上处理器是一种名为handler的特殊的挂钩,因此声明处理器就是声明handler挂钩的一个实例:
ap_hook_handler(example_handler, NULL, NULL, APR_HOOK_MIDDLE);
该声明将在指定模块的register_hooks中被注册。
13.10.3 实现处理器
在注册处理器的时候我们可以看到当处理器被调用的时候,example_handler函数将被调用。所有的处理器函数基本上都具有相同的样式:
static int x_handler(request_rec *r)
{
…
Return OK;
}
函数具有唯一的参数request_rec,同时函数会返回OK或者DECLINE。如果处理器成功的处理结束就返回OK。如果处理器发现不是它能处理的请求则返回DECLINE,这跟普通的挂钩完全相同。
对于example_handler,它的主要任务就是返回给客户端固定的HTML报文。因此整个过程相对简单:
static int x_handler(request_rec *r)
{
if (strcmp(r->handler, "example-handler")) {
return DECLINED;
}
对于大部分处理器来说,尤其是类似于status_handler,info_handler等都需要首先判断请求中传递的处理器是否是指定的处理器。
/*
* We're about to start sending content, so we need to force the HTTP
* headers to be sent at this point. Otherwise, no headers will be sent
* at all. We can set any we like first, of course. **NOTE** Here's
* where you set the "Content-type" header, and you do so by putting it in
* r->content_type, *not* r->headers_out("Content-type"). If you don't
* set it, it will be filled in with the server's default type (typically
* "text/plain"). You *must* also ensure that r->content_type is lower
* case.
*
* We also need to start a timer so the server can know if the connexion
* is broken.
*/
ap_set_content_type(r, "text/html");
if (r->header_only) {
return OK;
}
如果客户端仅仅需要我们返回头信息,那么我们就没有必要继续发送后面的报文体。
ap_rputs(DOCTYPE_HTML_3_2, r);
ap_rputs("<HTML>\n", r);
ap_rputs(" <HEAD>\n", r);
ap_rputs(" <TITLE>mod_example Module Content-Handler Output\n", r);
ap_rputs(" </TITLE>\n", r);
ap_rputs(" </HEAD>\n", r);
ap_rputs(" <BODY>\n", r);
ap_rputs(" <H1><SAMP>mod_example</SAMP> Module Content-Handler Output\n", r);
ap_rputs(" </H1>\n", r);
ap_rputs(" <P>\n", r);
ap_rprintf(r, " Apache HTTP Server version: \"%s\"\n",
ap_get_server_version());
ap_rputs(" <BR>\n", r);
ap_rprintf(r, " Server built: \"%s\"\n", ap_get_server_built());
ap_rputs(" </P>\n", r);;
ap_rputs(" <P>\n", r);
ap_rputs(" The format for the callback trace is:\n", r);
ap_rputs(" </P>\n", r);
ap_rputs(" <DL>\n", r);
ap_rputs(" <DT><EM>n</EM>.<SAMP><routine-name>", r);
ap_rputs("(<routine-data>)</SAMP>\n", r);
ap_rputs(" </DT>\n", r);
ap_rputs(" <DD><SAMP>[<applies-to>]</SAMP>\n", r);
ap_rputs(" </DD>\n", r);
ap_rputs(" </DL>\n", r);
ap_rputs(" <P>\n", r);
ap_rputs(" The <SAMP><routine-data></SAMP> is supplied by\n", r);
ap_rputs(" the routine when it requests the trace,\n", r);
ap_rputs(" and the <SAMP><applies-to></SAMP> is extracted\n", r);
ap_rputs(" from the configuration record at the time of the trace.\n", r);
ap_rputs(" <STRONG>SVR()</STRONG> indicates a server environment\n", r);
ap_rputs(" (blank means the main or default server, otherwise it's\n", r);
ap_rputs(" the name of the VirtualHost); <STRONG>DIR()</STRONG>\n", r);
ap_rputs(" indicates a location in the URL or filesystem\n", r);
ap_rputs(" namespace.\n", r);
ap_rputs(" </P>\n", r);
ap_rprintf(r, " <H2>Static callbacks so far:</H2>\n <OL>\n%s </OL>\n",
“Test”);
ap_rputs(" <H2>Request-specific callbacks so far:</H2>\n", r);
ap_rputs(" <H2>Environment for <EM>this</EM> call:</H2>\n", r);
ap_rputs(" </BODY>\n", r);
ap_rputs("</HTML>\n", r);
return OK;
}
我们在前面设置的发送的内容格式是”text/html”,因此真正发送的时候我们就必须构造HTML报文。通过ap_rputs以及ap_rprintf函数可以将指定的字符串写入到当前的请求中。
13.11 CGI脚本
尽管在前面我们已经谈过多种内容处理器,包括default_handler,mod_alias等等。但是相对于CGI内容处理器而言,它们都只能算是非常简单的处理器。
在本章中我们首先描述CGI的一些基本概念,在后面的部分,我们将深入的讨论CGI处理器的实现细节。
13.11.1 CGI工作原理
CGI的工作原理非常的简单。当浏览器请求的是一个CGI脚本的时候,服务器将首先找到该文件,然后启动一个新的子进程,并在该子进程中运行该脚本文件。如果客户端需要提交额外的信息给服务器,比如POST,那么此时服务器就会通过标准输入文件描述符将请求的信息发送给脚本。反之,如果脚本执行完毕后需要返回数据给客户端浏览器,那么它也会将输出内容写入到标准输出文件描述符,然后服务器读取该内容并发送给客户。客户端,Apache服务器以及CGI进程之间的关系可以用下图进行描述:
图13-
CGI脚本可以采用各种语言编写,但是大多数情况下会采用一些脚本语言,比如Perl,Python,甚至可以用Unix Shell进行编写。
13.11.2 CGI的相关配置
由于CGI属于处理器的一种,因此在使用之前必须能够对它进行配置处理,通知服务器哪些文件必须被CGI脚本处理。Apache中可以通过两种途径来建立这种对应的处理关系:
(1)ScriptAlias指令
ScriptAlias URL-path file-path|directory-path
这个指令在mod_alias模块中实现,用于映射一个URL到文件系统。该指令具有两个参数:将要用于访问这个目录的URI以及目录本身。以URL-path开头的(%已解码的)的URL会被映射到由第二个参数指定的具有完整路径名的本地文件系统中的脚本。ScriptAlias的标准配置如下:
ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin
这意味着任何针对/cgi-bin/ URI位置中的请求都会被视为CGI脚本处理。不过这也可能会带来一个潜在的问题。比如如果一个图片文件正好位于对应的目录中,那么该图片也会被视为CGI脚本来执行。显然这是错误的做法。
(2)目录内配置CGI脚本
如果想把ScriptAlias指令的目录之外的其余目录中文件视为CGI脚本,那么可以将CGI相关的配置放到该目录对应.htacess中或者httpd.conf文件的对应的<Directory>配置段中。相关的配置命令包括:
Options命令
通过Options命令的ExecCGI参数通知服务器该目录下的文件可以视为CGI脚本
AddHandler
Options只是通知服务器该目录下的文件可以被视为CGI脚本执行,但是具体哪个文件会被视为CGI脚本,服务器还不清楚。此时还需要通过AddHandler指令在文件类型和文件处理器之间建立对应的关系,比如:AddHandler cgi-script cgi。
该指令指令所有的xxx.cgi的文件都由处理器cgi-script进行处理。大部分情况下,用户都会使用通用的.cgi扩展名称来命名他们的cgi脚本,这就允许使用简单的命令海为整个服务器配置所有的cgi脚本。
ScriptInterpreterSource
对于Unix操作系统而言,上面的两个指令足够。但是对于window还需要一个额外的ScriptInterpreterSource指令。在CGI脚本中,通常在第一行会使用#!指令执行该脚本的解释器,比如#!/usr/local/perl则指令使用perl解释器。不过Window并不支持#!行,为此,Apache引入了ScriptInterpreterSource指令用以指令解释器,比如:ScriptInterpreterSource registry。
该指令使得服务器将在window的注册表中去查找与指令的文件关联的处理器程序。
13.11.3 CGI脚本示例
通过上面的配置,我们现在可以运行一些CGI脚本。在运行脚本之前,我们必须能够编写脚本。我们将使用两种语言来编写脚本:C语言和Perl脚本。
13.11.3.1 Helloworld
第一个CGI脚本非常简单,就是在浏览器上输出字符串“HelloWorld”。
Perl脚本:
#!/usr/bin/perl
print “Content-Type: text/plain\n\n”
print “Hello World\n”
C语言版本:
#include < stdio.h >
#include < stdib.h >
void main()
{
printf(″Contenttype:text/plain\n\n″);
printf("HelloWorld");
fflush(stdout);
}
对于脚本语言而言,需要注意的第一件事情就是指定需要调用的脚本解释器。对于Perl语言而言,大部分的Perl脚本都会使用PATH环境变量来找到Perl解释器,所以#!/usr/bin/perl可以简化为#!perl。这种简化方式有的时候会存在一定的问题。CGI脚本通常不会使用与常规用户相同的环境来运行。从安全的角度而言,将CGI脚本环境从Web服务器中剥离出来是个明智的选择。因此最好的办法就是指定解释器的完整的路径名称。
第二个问题就是CGI会在输出实际内容之前首先输出一些头信息。这些头信息很重要:
prinft (″Contenttype:text/plain\n\n″);
此行通过标准输出将字符串″Contenttype:text/plainnn″传送给Web服务器。它是一个MIME头信息,它告诉Web服务器随后的输出是以纯ASCII文本的形式。如果没有这个头信息,请求就会失败。当然CGI还会输出更多的信息,重要的是,脚本必须以空行终止头信息,这就是为什么用户在头信息之后会使用两个”\n”的原因。第一个”\n”用于终止内容类型(Content-Type)。HTTP协议规定头信息必须以”\n”结束。第二个”\n”用于终止头信息块。第二个”\n”之后的内容都将被认为是主体数据。
对于C语言编写的CGI,在编译通过后,将它们的后缀名称修改为.cgi,然后保存到/cgi-bin/目录中,这样第一个CGI程序就结束了。
13.11.3.2 输出当前的所有环境变量
下面的例子用于在浏览器中输出服务器中所有的环境变量的信息:
Perl脚本版本:
#!/usr/bin/perl
print “Content-Type: text/plain\n\n”;
foreach $var(sort(keys(%ENV))){
$val = $ENV($var};
$val =~ s|\n|\\n|g;
$val =~ s|"|\\"|g;
print "$s{var} = \"${var}\"\n";
}
C语言版本:
#include<stdio.h>
extern char **environ;
int main(int argc, int **argv)
{
int i;
if( environ != NULL)
for (i = 0; environ[i] != NULL; i++)
printf("%s\n", environ[i]);
return 0;
}
13.11.4 配置CGI处理器
13.11.5 mod_cgi模块分析
本章的最后部分我们将分析cgi处理器的实现细节。cgi处理器在模块mod_cgi中实现。
13.11.5.1 模块结构
module AP_MODULE_DECLARE_DATA cgi_module =
{
STANDARD20_MODULE_STUFF,
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
create_cgi_config, /* server config */
merge_cgi_config, /* merge server config */
cgi_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};
mod_cgi是一个常规的Apache2.0模块。这里没有目录配置指令,所有两个目录配置挂钩函数都被设置为NULL。不过这边有服务器相关的指令,而且继承规则也不简单,所以需要一个创建函数和一个合并函数。
cgi_cmds中定义了该模块能够处理的所有的命令:
static const command_rec cgi_cmds[] =
{
AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF,
"the name of a log for script debugging info"),
AP_INIT_TAKE1("ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF,
"the maximum length (in bytes) of the script debug log"),
AP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF,
"the maximum size (in bytes) to record of a POST request"),
{NULL}
};
mod_cgi模块本身需要处理的三个指令都是跟CGI日志相关的。对于web服务器而言最难诊断的内容之一就是CGI脚本。为了帮助诊断CGI脚本,Apache中提供了上面的三个指令。ScriptLog定义CGI日志文件;ScriptLogLength则定义了CGI日志文件的最大,当日志长度达到这个最大长度的时候,服务器就会停止写入CGI脚本的日志信息。ScriptBuffer则定义了能够记录到文件的PUT或者POST的请求主体的长度。
mod_cgi中关心的挂钩都在register_hooks中注册:
static void register_hooks(apr_pool_t *p)
{
static const char * const aszPre[] = { "mod_include.c", NULL };
ap_hook_handler(cgi_handler, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_post_config(cgi_post_config, aszPre, NULL, APR_HOOK_REALLY_FIRST);
}
针对这个模块的挂钩函数非常简单,一个就是内容处理器,负责生成内容;另外一个就是 post_config挂钩,用于在处理完配置文件之后进行调用。Post_config会使用mod_include模块注册一个函数,因此它必须在mod_include之后才能被调用。
13.11.5.2 处理器函数
处理器函数在每次CGI请求的时候会被调用,这个函数相对非常的复杂。我们需要逐步分析它。
fon
分享到:
相关推荐
在本文档生成器的POM文件中,定义了多个插件,包括Apache Maven Compiler Plugin和Apache Maven Build Plugin等。 五、Java语言 Java是一种面向对象的编程语言,广泛应用于软件开发领域。本文档生成器使用Java语言...
3. **配置DocumentRoot**:定义每个站点的根目录,存放网页内容的地方。 4. **设置ServerName**:指定每个虚拟主机对应的域名。 5. **权限与安全**:根据需求配置访问控制,例如`<Directory>`指令来限制对特定目录的...
OpenSSL 证书生成器 可用于apache的ssl证书生成
常见的开源代码生成器有JHipster、Apache Velocity和Dojo Builder等。 总之,代码生成器是现代软件开发中的重要工具,它通过自动化代码生成帮助开发者提升效率,降低错误,同时保持代码的一致性和可维护性。了解和...
本资源包“Golang_GoHTTP负载生成器ApacheBench ab替代”提供了一个用Go语言编写的HTTP负载生成器,作为ApacheBench的替代品。 这个负载生成器基于Go标准库中的"net/http"包,它允许开发者创建自定义的并发请求模式...
"天智Apache日志分析器"是一款专为这项任务设计的工具,由VC++编程语言实现,提供免费且高效的服务。 Apache日志主要记录了服务器接收到的所有请求,包括但不限于用户访问的页面、访问时间、请求方法、HTTP状态码、...
3. **Spring框架依赖**:虽然MP代码生成器不一定直接依赖Spring,但在许多实际项目中,它可能会与Spring集成,以便利用Spring的DI功能。因此,如果项目中使用了Spring,你需要确保已经添加了Spring的核心库。 4. **...
### 使用OpenSSL为Apache生成SSL证书 #### 一、引言 随着互联网技术的发展与用户对数据安全意识的提升,HTTPS协议已经成为网站标配之一。HTTPS不仅能够加密传输数据,还能验证服务器身份,确保用户访问的是真实...
代码生成器: 该项目为代码生成器 基于Apache Velocity的 Java模板引擎
1. **选择合适的代码生成器**:市面上有许多开源和商业的代码生成器可供选择,如MyBatis Generator、JPA Buddy、Apache Velocity等,每种都有其特点和适用场景,需要根据项目的技术栈和需求来挑选。 2. **配置参数**...
代码生成器则是开发工具中的一种,可以自动化地创建常见代码,提高开发效率。 FreeMarker是一个强大的、基于模板的Java模板引擎,用于生成动态内容。它主要被用来生成HTML页面或者其他任何类型文本,如电子邮件、...
接着,运行Java实体生成器,指定模板文件路径,程序会根据模板内容生成Java代码。生成的实体类可以直接在Eclipse或NetBeans这样的集成开发环境中导入,无需手动编写或复制粘贴代码,简化了开发流程。 【标签】"poi ...
"springmvc_dbutils_plus"可能是生成器的一个版本或特定功能,可能意味着该生成器除了SSM框架外,还支持使用DbUtils库进行数据库操作,DbUtils是Apache的一个简单数据库操作工具,可以简化JDBC操作。 总的来说,...
在IT领域,生成器是一种能够自动生成代码、文档、图像或其他内容的程序。在Java中,有许多库和框架可以帮助开发者创建这样的工具。例如,Apache Velocity或FreeMarker可以用来生成文本模板,如HTML或XML;而Java的...
3. **示例**:展示如何使用代码生成器的样例项目或代码片段。 4. **文档**:包括README文件,提供了项目简介、使用指南、API参考等信息。 5. **测试**:单元测试和集成测试用例,确保代码生成器的正确性。 6. **构建...
3. 模板设计与管理:通过灵活的模板机制,代码生成器可以适应不同的编程规范和框架。开发者可以定制模板,以生成符合特定项目需求的代码,提高了代码生成的灵活性和适用性。 该系统采用Apache Velocity作为模板引擎...
在Web服务开发中,Apache CXF作为一个生成插件,可以帮助开发者快速地创建、部署和消费Web服务。以下是使用Apache CXF 2.7.7版的一些关键知识点: 1. **WSDL First Approach**:CXF支持基于WSDL(Web Service ...
在代码生成器中,FreeMarker模板可以用来定义Vert.x代码的结构和内容,根据用户输入的数据动态填充模板。 总结来说,这个项目是JavaFX8和Vert.x技术的完美结合,提供了一种直观的方式来加速Vert.x应用的开发过程。...
3. **安装Apache**:使用包管理器进行安装。对于基于Debian的系统: ``` sudo apt-get install apache2 ``` 对于基于RPM的系统: ``` sudo yum install httpd ``` 4. **启动Apache服务**:安装完成后,启动...
订单号随机生成器是一种软件工具,它主要用于生成唯一的、随机的订单编号,这对于团购商家或者电商平台来说至关重要。在处理大量订单时,一个清晰且独特的订单号可以帮助商家有效地追踪和管理交易,避免混淆或遗漏。...