模块目录

可在nginx-1.20.1/下创建目录ext/,专门存放自定义模块
如添加HTTP模块mytest,则在ext/下创建目录mytest

  1. mytest/
  2. ├── config
  3. └── 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
image.png

二、编辑config文件

配合configure脚本

  1. # 仅在configure执行时使用,一般设置为模块名称
  2. ngx_addon_name="ngx_http_demo_module"
  3. # 保存所有的HTTP模块名称,每个HTTP模块间由空格符相连,在重新设置HTTP_MODULES变量时,不要直接覆盖他,应该使用空格追加模块
  4. HTTP_MODULES="$HTTP_MODULES ngx_http_demo_module"
  5. # 指定扩展模块的源代码
  6. NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_demo_module.c"

三、编辑模块代码文件

  1. 使用static来优化代码

1. 定义HTTP模块ngx_http_demo_module

  1. static ngx_module_t ngx_http_demo_module = {
  2. NGX_MODULE_V1, // 固定
  3. &ngx_http_demo_module_ctx,
  4. ngx_http_demo_commands,
  5. NGX_HTTP_MODULE, // 固定
  6. NULL,
  7. NULL,
  8. NULL,
  9. NULL,
  10. NULL,
  11. NULL,
  12. NULL,
  13. NGX_MODULE_V1_PADDING // 固定
  14. };

2. 定义模块上下文结构ngx_http_demo_module_ctx

  1. static ngx_http_module_t ngx_http_demo_module_ctx = {
  2. NULL, // preconfiguration
  3. NULL, // postconfiguration
  4. NULL, // create_main_conf
  5. NULL, // init_main_conf
  6. NULL, // create_srv_conf
  7. NULL, // merge_srv_conf
  8. NULL, // create_loc_conf
  9. NULL, // merge_loc_conf
  10. };

3. 定义模块命令结构ngx_http_demo_commands

  1. static ngx_command_t ngx_http_demo_commands[] = {
  2. {
  3. ngx_string("http_demo_module_test_cmd"),
  4. NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
  5. ngx_http_demo_set,
  6. NGX_HTTP_LOC_CONF_OFFSET,
  7. 0,
  8. NULL
  9. },
  10. ngx_null_command // 固定, 类似C字符串'\0'
  11. };

4. 编写命令触发回调函数ngx_http_demo_set

  1. // 声明
  2. static char *ngx_http_demo_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
  3. // 定义
  4. static char *ngx_http_demo_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
  5. ngx_http_core_loc_conf_t *clcf;
  6. clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
  7. clcf->handler = ngx_http_demo_handler;
  8. return NGX_OK;
  9. }

5. 编写HTTP请求处理回调函数ngx_http_demo_handler

  1. // 声明
  2. static ngx_int_t ngx_http_demo_handler(ngx_http_request_t *r);
  3. // 定义
  4. static ngx_int_t ngx_http_demo_handler(ngx_http_request_t *r) {
  5. return NGX_OK;
  6. }
  7. static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
  8. {
  9. // 必须是 GET 获得 HEAD 方法
  10. if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD)))
  11. {
  12. return NGX_HTTP_NOT_ALLOWED;
  13. }
  14. // 丢弃请求中的包体
  15. ngx_int_t rc = ngx_http_discard_request_body(r);
  16. if (rc != NGX_OK)
  17. {
  18. return rc;
  19. }
  20. ngx_str_t type = ngx_string("text/plain");
  21. ngx_str_t response = ngx_string("Hello mytest Module!\n");
  22. r->headers_out.status = NGX_HTTP_OK;
  23. r->headers_out.content_length_n = response.len;
  24. r->headers_out.content_type = type;
  25. // 发送HTTP头部
  26. rc = ngx_http_send_header(r);
  27. if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
  28. {
  29. return rc;
  30. }
  31. // 构造包体
  32. ngx_buf_t *buf;
  33. buf = ngx_create_temp_buf(r->pool, response.len);
  34. if (buf == NULL)
  35. {
  36. return NGX_HTTP_INTERNAL_SERVER_ERROR;
  37. }
  38. ngx_memcpy(buf->pos, response.data, response.len);
  39. buf->last = buf->pos + response.len;
  40. buf->last_buf = 1; // ?
  41. // 构造发送时的 ngx_chain_t 结构体
  42. ngx_chain_t out;
  43. out.buf = buf;
  44. out.next = NULL;
  45. // 发送包体
  46. return ngx_http_output_filter(r, &out);
  47. }

6. 添加头文件

核心3个文件,再缺什么补什么

  1. #include <ngx_config.h>
  2. #include <ngx_core.h>
  3. #include <ngx_http.h>

6. 完整代码

ngx_http_mytest_module.c内容如下:

  1. #include <ngx_config.h>
  2. #include <ngx_core.h>
  3. #include <ngx_http.h>
  4. // 回调函数
  5. static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r);
  6. static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
  7. static ngx_http_module_t ngx_http_mytest_module_ctx = {
  8. NULL,
  9. NULL,
  10. NULL,
  11. NULL,
  12. NULL,
  13. NULL,
  14. NULL,
  15. NULL};
  16. static ngx_command_t ngx_http_mytest_commands[] = {
  17. {
  18. ngx_string("mytest"),
  19. NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,
  20. ngx_http_mytest,
  21. NGX_HTTP_LOC_CONF_OFFSET,
  22. 0,
  23. NULL,
  24. },
  25. ngx_null_command};
  26. ngx_module_t ngx_http_mytest_module = {
  27. NGX_MODULE_V1,
  28. &ngx_http_mytest_module_ctx,
  29. ngx_http_mytest_commands,
  30. NGX_HTTP_MODULE,
  31. NULL,
  32. NULL,
  33. NULL,
  34. NULL,
  35. NULL,
  36. NULL,
  37. NULL,
  38. NGX_MODULE_V1_PADDING};
  39. // 处理nginx.conf配置项参数
  40. static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  41. {
  42. ngx_http_core_loc_conf_t *clcf;
  43. // 获取 mytest 配置项所属的配置块
  44. clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
  45. // 如果请求的主机域名、URI与mytest配置项所在的配置块相匹配,调用该方法
  46. clcf->handler = ngx_http_mytest_handler;
  47. return NGX_OK;
  48. }
  49. static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
  50. {
  51. // 必须是 GET 获得 HEAD 方法
  52. if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD)))
  53. {
  54. return NGX_HTTP_NOT_ALLOWED;
  55. }
  56. // 丢弃请求中的包体
  57. ngx_int_t rc = ngx_http_discard_request_body(r);
  58. if (rc != NGX_OK)
  59. {
  60. return rc;
  61. }
  62. ngx_str_t type = ngx_string("text/plain");
  63. ngx_str_t response = ngx_string("Hello mytest Module!\n");
  64. r->headers_out.status = NGX_HTTP_OK;
  65. r->headers_out.content_length_n = response.len;
  66. r->headers_out.content_type = type;
  67. // 发送HTTP头部
  68. rc = ngx_http_send_header(r);
  69. if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
  70. {
  71. return rc;
  72. }
  73. // 构造包体
  74. ngx_buf_t *buf;
  75. buf = ngx_create_temp_buf(r->pool, response.len);
  76. if (buf == NULL)
  77. {
  78. return NGX_HTTP_INTERNAL_SERVER_ERROR;
  79. }
  80. ngx_memcpy(buf->pos, response.data, response.len);
  81. buf->last = buf->pos + response.len;
  82. buf->last_buf = 1; // ?
  83. // 构造发送时的 ngx_chain_t 结构体
  84. ngx_chain_t out;
  85. out.buf = buf;
  86. out.next = NULL;
  87. // 发送包体
  88. return ngx_http_output_filter(r, &out);
  89. }

四、编译HTTP模块

  1. cd nginx-1.20.1/
  2. ./configure --add-module=ext/mytest/ # 模块目录
  3. make && make install

编译过程

添加自定义模块目录路径:绝对路径or相对路径,如ext/ngx_http_demo_module/

  1. ./configure --add-module={MODULE_DIR_PATH}
  2. make
  3. sudo make install

修改Nginx配置

  1. worker_processes 4;
  2. events {
  3. worker_connections 1024;
  4. }
  5. http {
  6. server {
  7. listen 8080;
  8. # http://192.168.132.128:8080/http_demo
  9. location /http_demo {
  10. demo_cmd;
  11. }
  12. }
  13. }

五、测试HTTP模块

  1. 网页访问:127.0.0.1:8080/http_demo
  2. 命令行访问:curl 127.0.0.1;8080/http_demo