Apache 模块开发 #
模块开发概述 #
Apache 模块架构 #
text
┌─────────────────────────────────────────────────────────────┐
│ Apache 模块架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Apache 核心 │
│ ├── HTTP 协议处理 │
│ ├── 连接管理 │
│ ├── 配置解析 │
│ └── 内存池管理 │
│ │
│ 模块系统 │
│ ├── 钩子机制(Hook) │
│ ├── 过滤器链(Filter) │
│ ├── 内容处理器(Handler) │
│ └── 配置指令(Directive) │
│ │
│ 模块类型 │
│ ├── 内容生成模块 │
│ ├── 认证授权模块 │
│ ├── 过滤器模块 │
│ └── 日志模块 │
│ │
└─────────────────────────────────────────────────────────────┘
请求处理流程 #
text
┌─────────────────────────────────────────────────────────────┐
│ 请求处理流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 连接阶段 │
│ create_connection │
│ └── 建立连接 │
│ │
│ 2. 请求解析阶段 │
│ pre_read_request │
│ read_request │
│ post_read_request │
│ └── 解析 HTTP 请求 │
│ │
│ 3. URL 转换阶段 │
│ translate_name │
│ map_to_storage │
│ └── URL 到文件路径转换 │
│ │
│ 4. 认证授权阶段 │
│ check_user_id │
│ auth_checker │
│ access_checker │
│ └── 用户认证和授权 │
│ │
│ 5. 内容类型阶段 │
│ type_checker │
│ fixups │
│ └── 确定内容类型 │
│ │
│ 6. 内容生成阶段 │
│ handler │
│ └── 生成响应内容 │
│ │
│ 7. 日志阶段 │
│ logging │
│ └── 记录访问日志 │
│ │
└─────────────────────────────────────────────────────────────┘
开发环境准备 #
安装开发工具 #
bash
# Ubuntu/Debian
sudo apt install apache2-dev build-essential
# CentOS/RHEL
sudo yum install httpd-devel gcc make
# 安装 APR 开发库
sudo apt install libapr1-dev libaprutil1-dev
开发目录结构 #
text
mod_example/
├── mod_example.c # 模块源代码
├── config.m4 # 构建配置
├── Makefile # 编译规则
└── README.md # 文档
简单模块示例 #
Hello World 模块 #
c
/* mod_hello.c - 简单的 Hello World 模块 */
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
/* 内容处理器函数 */
static int hello_handler(request_rec *r)
{
/* 检查请求方法 */
if (strcmp(r->handler, "hello")) {
return DECLINED;
}
/* 设置内容类型 */
r->content_type = "text/html";
/* 输出内容 */
ap_rputs("<html><body>", r);
ap_rputs("<h1>Hello, Apache Module!</h1>", r);
ap_rputs("</body></html>", r);
return OK;
}
/* 注册钩子 */
static void hello_register_hooks(apr_pool_t *p)
{
ap_hook_handler(hello_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
/* 模块定义 */
module AP_MODULE_DECLARE_DATA hello_module = {
STANDARD20_MODULE_STUFF,
NULL, /* 创建目录配置 */
NULL, /* 合并目录配置 */
NULL, /* 创建服务器配置 */
NULL, /* 合并服务器配置 */
NULL, /* 配置指令表 */
hello_register_hooks /* 注册钩子 */
};
编译模块 #
bash
# 使用 apxs 编译
apxs -c mod_hello.c
# 安装模块
sudo apxs -i -a mod_hello.la
# 或使用 Makefile
# Makefile 内容
APXS = apxs
all: mod_hello.la
mod_hello.la: mod_hello.c
$(APXS) -c mod_hello.c
install: mod_hello.la
$(APXS) -i -a mod_hello.la
配置使用模块 #
apache
# 加载模块
LoadModule hello_module modules/mod_hello.so
# 配置处理器
<Location /hello>
SetHandler hello
</Location>
钩子函数详解 #
常用钩子 #
c
/* ============================================
* 常用钩子函数
* ============================================ */
/* 1. 内容处理器钩子 */
static int my_handler(request_rec *r)
{
/* 处理请求并生成响应 */
return OK; /* 或 DECLINED, HTTP_OK, HTTP_NOT_FOUND 等 */
}
/* 2. 访问检查钩子 */
static int my_access_checker(request_rec *r)
{
/* 检查访问权限 */
if (/* 允许访问 */) {
return OK;
}
return HTTP_FORBIDDEN;
}
/* 3. 认证钩子 */
static int my_check_user_id(request_rec *r)
{
/* 验证用户身份 */
return OK;
}
/* 4. 授权钩子 */
static int my_auth_checker(request_rec *r)
{
/* 检查用户权限 */
return OK;
}
/* 5. URL 转换钩子 */
static int my_translate_name(request_rec *r)
{
/* 转换 URL */
return OK;
}
/* 6. 日志钩子 */
static int my_logger(request_rec *r)
{
/* 记录日志 */
return OK;
}
/* 7. 后置读取请求钩子 */
static int my_post_read_request(request_rec *r)
{
/* 请求读取后的处理 */
return OK;
}
注册钩子 #
c
static void my_register_hooks(apr_pool_t *p)
{
/* 注册处理器钩子 */
ap_hook_handler(my_handler, NULL, NULL, APR_HOOK_MIDDLE);
/* 注册访问检查钩子 */
ap_hook_access_checker(my_access_checker, NULL, NULL, APR_HOOK_MIDDLE);
/* 注册认证钩子 */
ap_hook_check_user_id(my_check_user_id, NULL, NULL, APR_HOOK_MIDDLE);
/* 注册授权钩子 */
ap_hook_auth_checker(my_auth_checker, NULL, NULL, APR_HOOK_MIDDLE);
/* 注册 URL 转换钩子 */
ap_hook_translate_name(my_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
/* 注册日志钩子 */
ap_hook_log_transaction(my_logger, NULL, NULL, APR_HOOK_MIDDLE);
/* 注册后置读取请求钩子 */
ap_hook_post_read_request(my_post_read_request, NULL, NULL, APR_HOOK_MIDDLE);
}
配置指令 #
定义配置指令 #
c
/* ============================================
* 配置指令定义
* ============================================ */
/* 模块配置结构 */
typedef struct {
const char *message;
int enabled;
} hello_config;
/* 创建目录配置 */
static void *hello_create_dir_config(apr_pool_t *p, char *dir)
{
hello_config *cfg = apr_pcalloc(p, sizeof(hello_config));
cfg->message = "Hello, World!";
cfg->enabled = 0;
return cfg;
}
/* HelloMessage 指令处理函数 */
static const char *set_hello_message(cmd_parms *cmd, void *mconfig, const char *arg)
{
hello_config *cfg = (hello_config *)mconfig;
cfg->message = arg;
return NULL;
}
/* HelloEnabled 指令处理函数 */
static const char *set_hello_enabled(cmd_parms *cmd, void *mconfig, int arg)
{
hello_config *cfg = (hello_config *)mconfig;
cfg->enabled = arg;
return NULL;
}
/* 配置指令表 */
static const command_rec hello_commands[] = {
AP_INIT_TAKE1("HelloMessage", set_hello_message, NULL, OR_ALL,
"Set the hello message"),
AP_INIT_FLAG("HelloEnabled", set_hello_enabled, NULL, OR_ALL,
"Enable or disable the hello module"),
{NULL}
};
使用配置 #
apache
# 在配置文件中使用
<Location /hello>
SetHandler hello
HelloMessage "Welcome to my site!"
HelloEnabled On
</Location>
请求对象 #
request_rec 结构 #
c
/* ============================================
* request_rec 常用成员
* ============================================ */
/* 请求信息 */
r->method; /* 请求方法:GET, POST 等 */
r->uri; /* 请求 URI */
r->filename; /* 文件路径 */
r->args; /* 查询字符串 */
r->protocol; /* 协议版本 */
r->hostname; /* 主机名 */
r->handler; /* 处理器名称 */
/* 请求头 */
r->headers_in; /* 请求头表 */
r->headers_out; /* 响应头表 */
r->err_headers_out; /* 错误响应头表 */
/* 连接信息 */
r->connection; /* 连接对象 */
r->server; /* 服务器对象 */
r->user; /* 认证用户名 */
r->ap_auth_type; /* 认证类型 */
/* 内容信息 */
r->content_type; /* 内容类型 */
r->content_encoding; /* 内容编码 */
r->content_languages; /* 内容语言 */
/* 状态 */
r->status; /* 响应状态码 */
r->status_line; /* 状态行 */
读取请求头 #
c
/* 获取请求头 */
const char *user_agent = apr_table_get(r->headers_in, "User-Agent");
const char *referer = apr_table_get(r->headers_in, "Referer");
const char *host = apr_table_get(r->headers_in, "Host");
设置响应头 #
c
/* 设置响应头 */
apr_table_set(r->headers_out, "X-Custom-Header", "value");
apr_table_set(r->headers_out, "Cache-Control", "no-cache");
/* 设置内容类型 */
r->content_type = "application/json";
输出响应内容 #
c
/* 输出字符串 */
ap_rputs("Hello, World!", r);
/* 输出格式化字符串 */
ap_rprintf(r, "Request URI: %s\n", r->uri);
/* 输出数据 */
ap_rwrite(data, len, r);
/* 刷新输出 */
ap_rflush(r);
内存管理 #
内存池 #
c
/* ============================================
* APR 内存池
* ============================================ */
/* 从内存池分配内存 */
void *ptr = apr_palloc(r->pool, size);
void *ptr = apr_pcalloc(r->pool, size); /* 初始化为 0 */
/* 字符串操作 */
char *str = apr_pstrdup(r->pool, "hello");
char *str = apr_pstrcat(r->pool, "hello", " ", "world", NULL);
char *str = apr_psprintf(r->pool, "Value: %d", value);
过滤器 #
输出过滤器 #
c
/* 输出过滤器函数 */
static apr_status_t my_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
{
apr_bucket *b;
for (b = APR_BRIGADE_FIRST(bb);
b != APR_BRIGADE_SENTINEL(bb);
b = APR_BUCKET_NEXT(b))
{
/* 处理每个 bucket */
if (!APR_BUCKET_IS_EOS(b)) {
const char *data;
apr_size_t len;
apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
/* 处理数据 */
}
}
return ap_pass_brigade(f->next, bb);
}
/* 注册过滤器 */
static void my_register_hooks(apr_pool_t *p)
{
ap_register_output_filter("MY_FILTER", my_output_filter,
NULL, AP_FTYPE_RESOURCE);
}
完整模块示例 #
c
/* mod_counter.c - 访问计数器模块 */
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
#include "apr_atomic.h"
/* 模块配置 */
typedef struct {
const char *header_name;
} counter_config;
/* 计数器 */
static volatile apr_uint32_t counter = 0;
/* 创建配置 */
static void *counter_create_dir_config(apr_pool_t *p, char *dir)
{
counter_config *cfg = apr_pcalloc(p, sizeof(counter_config));
cfg->header_name = "X-Request-Count";
return cfg;
}
/* 设置头名称指令 */
static const char *set_header_name(cmd_parms *cmd, void *mconfig, const char *arg)
{
counter_config *cfg = (counter_config *)mconfig;
cfg->header_name = arg;
return NULL;
}
/* 配置指令表 */
static const command_rec counter_commands[] = {
AP_INIT_TAKE1("CounterHeaderName", set_header_name, NULL, OR_ALL,
"Set the counter header name"),
{NULL}
};
/* 后置读取请求钩子 */
static int counter_post_read_request(request_rec *r)
{
counter_config *cfg = ap_get_module_config(r->per_dir_config, &counter_module);
/* 增加计数器 */
apr_uint32_t count = apr_atomic_inc32(&counter) + 1;
/* 设置响应头 */
char count_str[32];
snprintf(count_str, sizeof(count_str), "%u", count);
apr_table_set(r->headers_out, cfg->header_name, count_str);
return OK;
}
/* 注册钩子 */
static void counter_register_hooks(apr_pool_t *p)
{
ap_hook_post_read_request(counter_post_read_request, NULL, NULL, APR_HOOK_MIDDLE);
}
/* 模块定义 */
module AP_MODULE_DECLARE_DATA counter_module = {
STANDARD20_MODULE_STUFF,
counter_create_dir_config, /* 创建目录配置 */
NULL, /* 合并目录配置 */
NULL, /* 创建服务器配置 */
NULL, /* 合并服务器配置 */
counter_commands, /* 配置指令表 */
counter_register_hooks /* 注册钩子 */
};
调试技巧 #
日志输出 #
c
/* 使用 ap_log_error 记录日志 */
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Error message");
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, "Info: %s", value);
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Debug: uri=%s", r->uri);
使用 gdb 调试 #
bash
# 调试 Apache
sudo gdb httpd
# 设置断点
(gdb) break my_handler
(gdb) run -X
下一步 #
模块开发是 Apache 的高级主题,建议继续学习 PHP 集成 了解实际应用集成!
最后更新:2026-03-29