Nginx开发HTTP模块入门

Nginx开发HTTP模块入门

咱们以一个最简单的Hello World模块为例,学习Nginx的模块编写。假设咱们的模块在nginx配置文件中的指令名称为hello_world,那咱们就能够在nginx.conf文件中配置这个指令nginx

location / {
    hello_world;
}

这样,当咱们访问首页的时候就会执行hello_world指令,输出Hello World。接下来,就开始编写咱们的Hello World模块,按照nginx命名规则,咱们把这个模块取名为ngx_http_hello_world_moduleshell

编写config文件

咱们先建一个目录hello_world来保存咱们的扩展vim

mkdir /opt/nginx/ext/hello_world

要想一个办法把咱们的模块编译到nginx中,咱们须要在执行./configure命令的时候加入--add-module=PATH,这里的PATH就是咱们扩展目录,因此咱们要完成./configure须要使用的config文件,在扩展目录下,新建config文件,数组

vim /opt/nginx/ext/hello_world/config

内容以下框架

ngx_addon_name=ngx_http_hello_world_module
HTTP_MODULES="$HTTP_MODULES ngx_http_hello_world_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_world_module.c"
CORE_LIBS="$CORE_LIBS -lpcre"
  1. ngx_addon_name:仅在configure执行时使用,通常设置为模块名称
  2. HTTP_MODULES:保存全部的HTTP模块名称,每一个HTTP模块间由空格符相连,在从新设置HTTP_MODULES变量时,不要直接覆盖他,应该使用空格追加模块
  3. NGX_ADDON_SRCS:指定扩展模块的源代码

编写模块文件

编写好了config文件以后,就能够编写config文件中定义的模块文件了,新建模块文件curl

vim /opt/nginx/ext/hello_world/ngx_http_hello_world_module.c

定义HTTP模块

在nginx中,模块的数据类型是ngx_module_t
这个类型里面的成员详细能够参考源码,咱们直接写出ngx_http_hello_world_module模块定义异步

ngx_module_t  ngx_http_hello_world_module = {
    NGX_MODULE_V1,
    &ngx_http_hello_world_module_ctx,              /* module context */
    ngx_http_hello_world_commands,                 /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,             /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,             /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};
  1. NGX_MODULE_V1:表示版本号,使用内置的宏定义便可
  2. ngx_http_hello_world_module_ctx:这个成员很重要,设置上下文结构,HTTP框架要求,它必须指向ngx_http_module_t接口
  3. ngx_http_hello_world_commands:定义模块的配置文件参数,这个会做用在nginx.conf文件解析
  4. NGX_HTTP_MODULE:表示这是一个HTTP模块

编写模块上下文结构

这个结构体针对本文的HTTP模块来讲,不须要调用,设置为NULL便可,可是HTTP框架要求,它必须指向ngx_http_module_t接口,因此咱们这样定义函数

static ngx_http_module_t  ngx_http_hello_world_module_ctx = {
    NULL,                                  /* preconfiguration */
    NULL,                                  /* postconfiguration */
    NULL,                                  /* create main configuration */
    NULL,                                  /* init main configuration */
    NULL,                                  /* create server configuration */
    NULL,                                  /* merge server configuration */
    NULL,                                  /* create location configuration */
    NULL                                   /* merge location configuration */
};

编写命令结构

ngx_http_hello_world_commands数组用于定义模块的配置文件参数,每个元素都是ngx_command_t类型,数组的结尾用ngx_null_command表示。咱们编写的模块的命令结构以下post

static ngx_command_t  ngx_http_hello_world_commands[] = {
    { ngx_string("hello_world"),
      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
      ngx_http_hello_world,
      0,
      0,
      NULL },
    ngx_null_command
}
  1. hello_world:表示咱们的配置项名称,和gzip同样,设置名称而已
  2. NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS:指定出现的位置,能够出如今location{}块中
  3. ngx_http_hello_world:这是ngx_command_t中的set成员,当某块配置中出现hello_world时候,nginx将会启动ngx_http_hello_world方法,咱们须要实现该函数

编写触发回调函数

前面编写完命令结构,定义了出现hello_world配置项的时候触发ngx_http_hello_world方法,咱们要实现这个方法学习

static char * ngx_http_hello_world(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_hello_world_handler;
    return NGX_CONF_OK;
}
  1. ngx_http_conf_get_module_loc_conf:这个函数会首先找到hello_world配置项所属的配置块,而后赋值给变量clcf
  2. clcf->handler:HTTP框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时,若是请求的主机域名,URL和hello_wrold相匹配,那么就调用这handler指向的方法ngx_http_hello_world_handler

编写对http请求的具体处理方法hander

前面分析,当匹配URL以后,真正执行的实际上是咱们的ngx_http_hello_world_handler函数,因此,咱们要实现这个函数

static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
{
    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 World");
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = response.len;
    r->headers_out.content_type = type;

    rc = ngx_http_send_header(r);//发送头部
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    ngx_buf_t *b;
    b = ngx_create_temp_buf(r->pool, response.len);//异步发送,要用堆内存空间
    if (b == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    ngx_memcpy(b->pos, response.data, response.len);
    b->last = b->pos + response.len;
    b->last_buf = 1;

    ngx_chain_t out;
    out.buf = b;
    out.next = NULL;

    return ngx_http_output_filter(r, &out);//向用户发送响应包
}

这个函数的参数是ngx_http_request_t类型参数r,它包含了不少东西(方法,URI,协议,头部等)。

完整的例子

将上面的步骤联系起来,能够最终合并成咱们的新模块文件ngx_http_hello_world_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_command_t ngx_http_mytest_commands[] = {
    {
        ngx_string("hello_world"),
        NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,
        ngx_http_hello_world,
        NGX_HTTP_LOC_CONF_OFFSET,
        0,
        NULL
    },
    ngx_null_command
};

static ngx_http_module_t ngx_http_hello_world_module_ctx = {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
};

ngx_module_t ngx_http_hello_world_module = {
    NGX_MODULE_V1,
    &ngx_http_hello_world_module_ctx,
    ngx_http_hello_world_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};


static char * 
ngx_http_mytest(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_hello_world_handler;

    return NGX_CONF_OK;
}


static ngx_int_t ngx_http_hello_world_handler(ngx_http_request_t *r)
{
    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 World");
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = response.len;
    r->headers_out.content_type = type;

    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    ngx_buf_t *b;
    b = ngx_create_temp_buf(r->pool, response.len);
    if (b == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    ngx_memcpy(b->pos, response.data, response.len);
    b->last = b->pos + response.len;
    b->last_buf = 1;

    ngx_chain_t out;
    out.buf = b;
    out.next = NULL;

    return ngx_http_output_filter(r, &out);
}

编译HTTP模块

通过上面的步骤,咱们的扩展模块下面有两个文件在/opt/nginx/ext/hello_world目录中

shell> ls /opt/nginx/ext/hello_world
config
ngx_http_hello_world_module.c

咱们使用测试的nginx版本为nginx-1.0.10。在安装过程当中,执行./configure的时候须要加入咱们模块路径

shell> ./configure --add-module=/opt/nginx/ext/hello_world/nginx_hello_world/

而后进行安装便可

shell> make && make install

测试HTTP模块

修改nginx配置文件nginx.conf,若是不想配置规则,能够直接让访问首页的时候就执行hello_world执行

location / {
    hello_world;
}

而后启动咱们的nginx

shell> ./sbin/nginx

测试咱们的nginx扩展的HTTP模块

shell> curl "127.0.0.1"
Hello World
相关文章
相关标签/搜索