iT邦幫忙

0

nginx - 開發自己的module

key point

  • our module should be involved in session request.
  • understand nginx data structures.

classic request process

  1. worker process use event module to detect network events(for loop).
  2. event module receive SYN, establish TCP connection.
  3. parse nginx.conf, use related modules.

module naming
create file: src/http/modules/ngx_http_mytest_module.c

compile module to nginx
create file: /src/http/config 路徑不限, 定義以下三個變量

  • ngx_addon_name = ngx_http_mytest_module
  • HTTP_MODULES = "$HTTP_MODULE ngx_http_mytest_module" (contains all http module)
  • NGX_ADDON_SRCS = "$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mytest_module.c"

./configure --add-module=src/http/modules/
+ ngx_http_mytest_module was configured

或是另一種方法, ./configure之後,直接修改objs/ngx_modules.c和objs/Makefile
note: 完成之後要重新complie, make, make install

ngx_module_s

  • ctx_index: 當前module在這類module中的序號, ngx_module_ctx_index()
  • index: 當前module在ngx_modules[]中的序號, ngx_preinit_modules()
  • *ctx: context, 指向特定類型module的公共接口
  • ngx_command_t: 處理nginx.conf
  • type: module分類 (http, core, conf, event, mail, other...)
ngx_int_t
ngx_preinit_modules(void)
{
    ngx_uint_t  i;

    for (i = 0; ngx_modules[i]; i++) {
        ngx_modules[i]->index = i;
        ngx_modules[i]->name = ngx_module_names[i];
    }

    ngx_modules_n = i;
    ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES;

    return NGX_OK;
}

ngx_http_module_t
http框架在啟動過程中會在個階段調用ngx_http_module中相應的方法(8個回調函數), 其回調順序可能不同

ngx_command_s

  • name: 配置名
  • type: 出現位置, server or location...
  • *(set)(ngx_conf_t , ngx_command_t* , void*): 處理函數
  • conf: 在配置文件中的offset
  • offset: 配合conf, macro: offsetof
  • 最後加上ngx_null_command
static ngx_command_t ngx_http_mytest_commands[] = {
    {
        ngx_string("mytest"),
        NGX_HTTP_MAIN|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|
        NGX_HTTP_LMT_CONG|NGX_CONF_NOARGS,
        ngx_http_mytest,
        NGX_HTTP_LOC_CONF_OFFSET,
        0,
        NULL},
        ngx_null_command,
};

set function

static char* ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    /*每個http{}, server{}, location{}都有一個clcf結構*/
    ngx_http_core_loc_conf_t *clcf;
    
    /* clcf = cf->loc_conf[ngx_http_core_module.ctx_index]*/
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    
    /*HTTP框架處理請求進行到NGX_HTTP_CONTENT_PHASE時(共11階段,參考ngx_http_phases), 若匹配則調用*/
    clcf->handler = ngx_http_mytest_handler;
    return NGX_CONF_OK;
}

定義ngx_http_module_t 接口
這些函數在HTTP框架初始化時呼叫
https://ithelp.ithome.com.tw/upload/images/20180328/20109268zTyw0IesXK.png
定義mytest module

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
};

範例

static ngx_int_t ngx_http_mytest_handler(ngx_http_request *r)
{
    //只接受HTTP GET or HEAD
    if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {
        return NGX_HTTP_NOT_ALLOWED;
    }
    //將接收自client端的body丟掉
    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\n");
    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;
    //最後一塊buffer
    b->last_buf = 1;
    
    //chain用於buffer
    ngx_chain_t out;
    out.buf = b;
    out.next = NULL;
    
    return ngx_http_output_filter(r, &out);

test
nginx.conf:

http{
    ...
    server{
        ...
        location mytest{
            mytest;
        }
        ...
    }
    ...
}

command:
/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

brower 輸入 localhost/mytest


尚未有邦友留言

立即登入留言