Apache HTTP Server 採模組化架構,核心功能皆以模組形式提供。開發自訂模組可擴充伺服器能力,例如自定義路由處理、請求/回應攔截、認證授權等。本文透過兩個完整範例,示範內容生成器與輸出過濾器的開發流程。
apxsapxs (APache eXtenSion tool) 是編譯、安裝 DSO (Dynamic Shared Object) 模組的標準工具。
# CentOS/RHEL
yum install -y httpd-devel
# Ubuntu/Debian
apt-get install -y apache2-dev
# 驗證
apxs -q CC # 顯示編譯器
apxs -q CFLAGS # 顯示編譯旗標
apxs -g -n helloworld
產生 mod_helloworld.c、Makefile 等檔案。
static int helloworld_handler(request_rec *r) {
if (strcmp(r->handler, "helloworld")) return DECLINED; // 非本模組處理
r->content_type = "text/html";
if (!r->header_only)
ap_rputs("The sample page from mod_helloworld.c\n", r);
return OK;
}
static void helloworld_register_hooks(apr_pool_t *p) {
ap_hook_handler(helloworld_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
module AP_MODULE_DECLARE_DATA helloworld_module = {
STANDARD20_MODULE_STUFF,
NULL, NULL, NULL, NULL, NULL,
helloworld_register_hooks
};
apxs -c mod_helloworld.c # 編譯產生 .so
apxs -i mod_helloworld.la # 安裝到 modules/ 目錄
修改 httpd.conf:
LoadModule helloworld_module modules/mod_helloworld.so
<Location /helloworld>
SetHandler helloworld
</Location>
重啟 Apache:apachectl restart。瀏覽 https://localhost/helloworld 即可看到輸出。
過濾器位於「內容生成器」與「網路輸出」之間,可攔截、修改回應內容。適用於壓縮、加密、關鍵字替換等場景。
static apr_status_t CaseFilterOutFilter(ap_filter_t *f, apr_bucket_brigade *pbbIn) {
request_rec *r = f->r;
conn_rec *c = r->connection;
apr_bucket_brigade *pbbOut = apr_brigade_create(r->pool, c->bucket_alloc);
for (apr_bucket *bkt = APR_BRIGADE_FIRST(pbbIn);
bkt != APR_BRIGADE_SENTINEL(pbbIn);
bkt = APR_BUCKET_NEXT(bkt)) {
if (APR_BUCKET_IS_EOS(bkt)) { // 串流結束
APR_BRIGADE_INSERT_TAIL(pbbOut, apr_bucket_eos_create(c->bucket_alloc));
continue;
}
const char *data; apr_size_t len;
apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
char *buf = apr_bucket_alloc(len, c->bucket_alloc);
for (apr_size_t i = 0; i < len; ++i) buf[i] = apr_toupper(data[i]);
apr_bucket *out = apr_bucket_heap_create(buf, len, apr_bucket_free, c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(pbbOut, out);
}
apr_brigade_cleanup(pbbIn);
return ap_pass_brigade(f->next, pbbOut); // 傳遞給下一個過濾器
}
static void CaseFilterInsertFilter(request_rec *r) {
CaseFilterConfig *cfg = ap_get_module_config(r->server->module_config, &case_filter_module);
if (cfg->bEnabled)
ap_add_output_filter("CaseFilter", NULL, r, r->connection);
}
static const command_rec CaseFilterCmds[] = {
AP_INIT_FLAG("CaseFilter", CaseFilterEnable, NULL, RSRC_CONF, "Enable case filter"),
{ NULL }
};
static void CaseFilterRegisterHooks(apr_pool_t *p) {
ap_hook_insert_filter(CaseFilterInsertFilter, NULL, NULL, APR_HOOK_MIDDLE);
ap_register_output_filter("CaseFilter", CaseFilterOutFilter, NULL, AP_FTYPE_RESOURCE);
}
LoadModule case_filter_module modules/mod_casefilter.so
CaseFilter On
| 概念 | 說明 |
|---|---|
| Handler (內容生成器) | 產生回應主體,掛在 ap_hook_handler |
| Output Filter (輸出過濾器) | 處理回應串流,掛在 ap_hook_insert_filter + ap_register_output_filter |
| Bucket Brigade | Apache 的串流資料結構,由多個 bucket 串連 |
| apr_pool_t | 記憶體池,自動生命週期管理,避免洩漏 |
httpd.conf 中 LoadModule,且路徑正確EOS (End Of Stream) bucket,否則連線會掛住apr_pcalloc、apr_bucket_alloc 等 APR 函數分配記憶體,隨 pool 自動釋放LogLevel debug 觀察模組載入與掛鉤順序