模块目录
可在nginx-1.20.1/下创建目录ext/,专门存放自定义模块
如添加HTTP模块mytest,则在ext/下创建目录mytest
mytest/
├── config
└── ngx_http_mytest_module.c
步骤
原则:保证与Nginx命名规则一致,且文件目录代码中模块命名一致(太长也不要纠结),
demo
可修改成自定义模块名 以下全部基于项目目录/根目录:nginx-1.21.3/
一、创建相关文件目录
创建模块文件目录ngx_http_demo_module/
可在任意位置,推荐在
nginx-1.21.3/modules/
或nginx-1.21.3/ext/
中
创建模块代码文件ngx_http_demo_module.c
创建config文件config
二、编辑config文件
配合configure脚本
# 仅在configure执行时使用,一般设置为模块名称
ngx_addon_name="ngx_http_demo_module"
# 保存所有的HTTP模块名称,每个HTTP模块间由空格符相连,在重新设置HTTP_MODULES变量时,不要直接覆盖他,应该使用空格追加模块
HTTP_MODULES="$HTTP_MODULES ngx_http_demo_module"
# 指定扩展模块的源代码
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_demo_module.c"
三、编辑模块代码文件
- 使用
static
来优化代码
1. 定义HTTP模块ngx_http_demo_module
static ngx_module_t ngx_http_demo_module = {
NGX_MODULE_V1, // 固定
&ngx_http_demo_module_ctx,
ngx_http_demo_commands,
NGX_HTTP_MODULE, // 固定
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING // 固定
};
2. 定义模块上下文结构ngx_http_demo_module_ctx
static ngx_http_module_t ngx_http_demo_module_ctx = {
NULL, // preconfiguration
NULL, // postconfiguration
NULL, // create_main_conf
NULL, // init_main_conf
NULL, // create_srv_conf
NULL, // merge_srv_conf
NULL, // create_loc_conf
NULL, // merge_loc_conf
};
3. 定义模块命令结构ngx_http_demo_commands
static ngx_command_t ngx_http_demo_commands[] = {
{
ngx_string("http_demo_module_test_cmd"),
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
ngx_http_demo_set,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL
},
ngx_null_command // 固定, 类似C字符串'\0'
};
4. 编写命令触发回调函数ngx_http_demo_set
// 声明
static char *ngx_http_demo_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
// 定义
static char *ngx_http_demo_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_demo_handler;
return NGX_OK;
}
5. 编写HTTP请求处理回调函数ngx_http_demo_handler
// 声明
static ngx_int_t ngx_http_demo_handler(ngx_http_request_t *r);
// 定义
static ngx_int_t ngx_http_demo_handler(ngx_http_request_t *r) {
return NGX_OK;
}
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
{
// 必须是 GET 获得 HEAD 方法
if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD)))
{
return NGX_HTTP_NOT_ALLOWED;
}
// 丢弃请求中的包体
ngx_int_t rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK)
{
return rc;
}
ngx_str_t type = ngx_string("text/plain");
ngx_str_t response = ngx_string("Hello mytest Module!\n");
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = response.len;
r->headers_out.content_type = type;
// 发送HTTP头部
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
{
return rc;
}
// 构造包体
ngx_buf_t *buf;
buf = ngx_create_temp_buf(r->pool, response.len);
if (buf == NULL)
{
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_memcpy(buf->pos, response.data, response.len);
buf->last = buf->pos + response.len;
buf->last_buf = 1; // ?
// 构造发送时的 ngx_chain_t 结构体
ngx_chain_t out;
out.buf = buf;
out.next = NULL;
// 发送包体
return ngx_http_output_filter(r, &out);
}
6. 添加头文件
核心3个文件,再缺什么补什么
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
6. 完整代码
ngx_http_mytest_module.c内容如下:
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
// 回调函数
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r);
static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_http_module_t ngx_http_mytest_module_ctx = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL};
static ngx_command_t ngx_http_mytest_commands[] = {
{
ngx_string("mytest"),
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,
ngx_http_mytest,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL,
},
ngx_null_command};
ngx_module_t ngx_http_mytest_module = {
NGX_MODULE_V1,
&ngx_http_mytest_module_ctx,
ngx_http_mytest_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING};
// 处理nginx.conf配置项参数
static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
// 获取 mytest 配置项所属的配置块
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
// 如果请求的主机域名、URI与mytest配置项所在的配置块相匹配,调用该方法
clcf->handler = ngx_http_mytest_handler;
return NGX_OK;
}
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
{
// 必须是 GET 获得 HEAD 方法
if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD)))
{
return NGX_HTTP_NOT_ALLOWED;
}
// 丢弃请求中的包体
ngx_int_t rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK)
{
return rc;
}
ngx_str_t type = ngx_string("text/plain");
ngx_str_t response = ngx_string("Hello mytest Module!\n");
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = response.len;
r->headers_out.content_type = type;
// 发送HTTP头部
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
{
return rc;
}
// 构造包体
ngx_buf_t *buf;
buf = ngx_create_temp_buf(r->pool, response.len);
if (buf == NULL)
{
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_memcpy(buf->pos, response.data, response.len);
buf->last = buf->pos + response.len;
buf->last_buf = 1; // ?
// 构造发送时的 ngx_chain_t 结构体
ngx_chain_t out;
out.buf = buf;
out.next = NULL;
// 发送包体
return ngx_http_output_filter(r, &out);
}
四、编译HTTP模块
cd nginx-1.20.1/
./configure --add-module=ext/mytest/ # 模块目录
make && make install
编译过程
添加自定义模块目录路径:绝对路径or相对路径,如ext/ngx_http_demo_module/
./configure --add-module={MODULE_DIR_PATH}
make
sudo make install
修改Nginx配置
worker_processes 4;
events {
worker_connections 1024;
}
http {
server {
listen 8080;
# http://192.168.132.128:8080/http_demo
location /http_demo {
demo_cmd;
}
}
}
五、测试HTTP模块
- 网页访问:127.0.0.1:8080/http_demo
- 命令行访问:
curl 127.0.0.1;8080/http_demo