Nginx 模块有三种角色:
Client sends HTTP request → Nginx chooses the appropriate handler based on the location config →
(if applicable) load-balancer picks a backend server →
Handler does its thing and passes each output buffer to the first filter →
First filter passes the output to the second filter → second to third → third to fourth → etc.
→ Final response sent to client
typedef struct {
ngx_uint_t methods;
ngx_flag_t create_full_put_path;
ngx_uint_t access;
} ngx_http_dav_loc_conf_t;
Nginx 模块的数据结构如下定义:
/* Nginx 模块的数据结构 */
#define NGX_MODULE_V1 0, 0, 0, 0, 0, 0, 1
#define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0
struct ngx_module_s {
/* 模块类别由type成员决定,ctx_index表示当前模块在type类模块中的序号 */
ngx_uint_t ctx_index;
/* index 区别与ctx_index,index表示当前模块在所有模块中的序号 */
ngx_uint_t index;
/* spare 序列保留变量,暂时不被使用 */
ngx_uint_t spare0;
ngx_uint_t spare1;
ngx_uint_t spare2;
ngx_uint_t spare3;
/* 当前模块的版本 */
ngx_uint_t version;
/* ctx指向特定类型模块的公共接口,例如在HTTP模块中,ctx指向ngx_http_module_t结构体 */
void *ctx;
/* 处理nginx.conf中的配置项 */
ngx_command_t *commands;
/* type表示当前模块的类型 */
ngx_uint_t type;
/* 下面的7个函数指针是在Nginx启动或停止时,分别调用的7中方法 */
/* 在master进程中回调init_master */
ngx_int_t (*init_master)(ngx_log_t *log);
/* 初始化所有模块时回调init_module */
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
/* 在worker进程提供正常服务之前回调init_process初始化进程 */
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
/* 初始化多线程 */
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
/* 退出多线程 */
void (*exit_thread)(ngx_cycle_t *cycle);
/* 在worker进程停止服务之前回调exit_process */
void (*exit_process)(ngx_cycle_t *cycle);
/* 在master进程退出之前回调exit_master */
void (*exit_master)(ngx_cycle_t *cycle);
/* 保留字段,未被使用 */
uintptr_t spare_hook0;
uintptr_t spare_hook1;
uintptr_t spare_hook2;
uintptr_t spare_hook3;
uintptr_t spare_hook4;
uintptr_t spare_hook5;
uintptr_t spare_hook6;
uintptr_t spare_hook7;
};
在该数据结构中,其中最重要的是两个成员
ctx和
commands,这里两个成员会在分别在下面的模块配置指令和模块上下文中讲解;若是
HTTP 模块时,
type 字段必须定义为
NGX_HTTP_MODULE;
static ngx_command_t ngx_http_circle_gif_commands[] = {
{ ngx_string("circle_gif"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_circle_gif,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("circle_gif_min_radius"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_circle_gif_loc_conf_t, min_radius),
NULL },
...
ngx_null_command
};
ngx_command_t 类型定义在
core/ngx_conf_file.h:
struct ngx_command_s {
/* 配置项名称 */
ngx_str_t name;
/* 配置项类型,type将指定配置项可以出现的位置以及携带参数的个数 */
ngx_uint_t type;
/* 处理配置项的参数 */
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
/* 在配置文件中的偏移量,conf与offset配合使用 */
ngx_uint_t conf;
ngx_uint_t offset;
/* 配置项读取后的处理方法,必须指向ngx_conf_post_t 结构 */
void *post;
};
name :配置指令的名称;
NGX_HTTP_MAIN_CONF: directive is valid in the main config
NGX_HTTP_SRV_CONF: directive is valid in the server (host) config
NGX_HTTP_LOC_CONF: directive is valid in a location config
NGX_HTTP_UPS_CONF: directive is valid in an upstream config
NGX_CONF_NOARGS: directive can take 0 arguments
NGX_CONF_TAKE1: directive can take exactly 1 argument
NGX_CONF_TAKE2: directive can take exactly 2 arguments
…
NGX_CONF_TAKE7: directive can take exactly 7 arguments
NGX_CONF_FLAG: directive takes a boolean ("on" or "off")
NGX_CONF_1MORE: directive must be passed at least one argument
NGX_CONF_2MORE: directive must be passed at least two arguments
set :这是一个函数指针,当
Nginx 在解析配置时,若遇到该配置指令,将会把读取到的值传递给这个函数进行分解处理。因为具体每个配置指令的值如何处理,只有定义这个配置指令的人是最清楚的。来看一下这个函数指针要求的函数原型。
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
该函数处理成功时,返回
NGX_OK,否则返回
NGX_CONF_ERROR 或者是一个自定义的错误信息的字符串。该函数传入三个类型的参数:
typedef struct{/* 可以把不需要调用的函数指针设置为 NULL */
/* 解析配置文件之前被调用 */
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
/* 完成配置文件的解析后被调用 */
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
/* 创建存储main级别的全局配置项的结构体(直属于http块) */
void *(*create_main_conf)(ngx_conf_t *cf);
/* 初始化main级别的配置项 */
char *(*init_main_conf)(ngx_conf_t *cf);
/* 创建存储srv级别的配置项的结构体(直属于server块) */
void *(*create_srv_conf)(ngx_conf_t *cf);
/* 合并main级别与srv级别下的同名配置项 */
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
/* 创建存储loc级别的配置项的结构体(直属于location块) */
void *(*create_loc_conf)(ngx_conf_t *cf);
/* 合并srv级别与loc级别下的同名配置项 */
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
}ngx_http_module_t;
static ngx_http_module_t ngx_http_circle_gif_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_circle_gif_create_loc_conf, /* create location configuration */
ngx_http_circle_gif_merge_loc_conf /* merge location configuration */
};
下面针对最后两项进行说明,以下是以
circle_gif 模块为例子,该 模块源码;
static void *
ngx_http_circle_gif_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_circle_gif_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_circle_gif_loc_conf_t));
if (conf == NULL) {
return NGX_CONF_ERROR;
}
conf->min_radius = NGX_CONF_UNSET_UINT;
conf->max_radius = NGX_CONF_UNSET_UINT;
return conf;
}
static char *
ngx_http_circle_gif_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_circle_gif_loc_conf_t *prev = parent;
ngx_http_circle_gif_loc_conf_t *conf = child;
ngx_conf_merge_uint_value(conf->min_radius, prev->min_radius, 10);
ngx_conf_merge_uint_value(conf->max_radius, prev->max_radius, 20);
if (conf->min_radius < 1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"min_radius must be equal or more than 1");
return NGX_CONF_ERROR;
}
if (conf->max_radius < conf->min_radius) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"max_radius must be equal or more than min_radius");
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
ngx_module_t ngx_http__module = {
NGX_MODULE_V1,
&ngx_http__module_ctx, /* module context */
ngx_http__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
};
typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
其中
r 是
request 结构
http 请求,包含客户端请求所有的信息,例如:
request method, URI, and headers。 该函数处理成功返回
NGX_OK,处理发生错误返回
NGX_ERROR,拒绝处理(留给后续的
Handler 进行处理)返回
NGX_DECLINE。 返回
NGX_OK 也就代表给客户端的响应已经生成,否则返回
NGX_ERROR 就发生错误了。
static ngx_int_t
ngx_http_circle_gif_handler(ngx_http_request_t *r)
{
ngx_http_circle_gif_loc_conf_t *circle_gif_config;
circle_gif_config = ngx_http_get_module_loc_conf(r, ngx_http_circle_gif_module);
...
}
typedef struct {
...
/* the memory pool, used in the ngx_palloc functions */
ngx_pool_t *pool;
ngx_str_t uri;
ngx_str_t args;
ngx_http_headers_in_t headers_in;
...
} ngx_http_request_t;
其中参数的意义如下:
typedef stuct {
...
ngx_uint_t status;
size_t content_type_len;
ngx_str_t content_type;
ngx_table_elt_t *content_encoding;
off_t content_length_n;
time_t date_time;
time_t last_modified_time;
..
} ngx_http_headers_out_t;
例如,一个模块设置为
Content-Type to "image/gif", Content-Length to 100, and return a 200 OK response,则其实现为:
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = 100;
r->headers_out.content_type.len = sizeof("image/gif") - 1;
r->headers_out.content_type.data = (u_char *) "image/gif";
ngx_http_send_header(r);
假如content_encoding 是 (
ngx_table_elt_t*)类型时,则模块需要为这些类型分配内存,可以调用
ngx_list_push 函数,实现如下:
r->headers_out.content_encoding = ngx_list_push(&r->headers_out.headers);
if (r->headers_out.content_encoding == NULL) {
return NGX_ERROR;
}
r->headers_out.content_encoding->hash = 1;
r->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1;
r->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding";
r->headers_out.content_encoding->value.len = sizeof("deflate") - 1;
r->headers_out.content_encoding->value.data = (u_char *) "deflate";
ngx_http_send_header(r);
ngx_buf_t *b;
ngx_chain_t out;
2、然后分配缓冲区 buffer,使响应数据指向它:
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"Failed to allocate response buffer.");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->pos = some_bytes; /* first position in memory of the data */
b->last = some_bytes + some_bytes_length; /* last position */
b->memory = 1; /* content is in read-only memory */
/* (i.e., filters should copy it rather than rewrite in place) */
b->last_buf = 1; /* there will be no more buffers in the request */
3、接着,把模块挂载到 chain link 上:
out.buf = b;
out.next = NULL;
4、最后,发送包体:
return ngx_http_output_filter(r, &out);
NGX_HTTP_POST_READ_PHASE:
/* 读取请求内容阶段 */
NGX_HTTP_SERVER_REWRITE_PHASE:
/* Server请求地址重写阶段 */
NGX_HTTP_FIND_CONFIG_PHASE:
/* 配置查找阶段: */
NGX_HTTP_REWRITE_PHASE:
/* Location请求地址重写阶段 */
NGX_HTTP_POST_REWRITE_PHASE:
/* 请求地址重写提交阶段 */
NGX_HTTP_PREACCESS_PHASE:
/* 访问权限检查准备阶段 */
NGX_HTTP_ACCESS_PHASE:
/* 访问权限检查阶段 */
NGX_HTTP_POST_ACCESS_PHASE:
/* 访问权限检查提交阶段 */
NGX_HTTP_TRY_FILES_PHASE:
/* 配置项try_files处理阶段 */
NGX_HTTP_CONTENT_PHASE:
/* 内容产生阶段 */
NGX_HTTP_LOG_PHASE:
/* 日志模块处理阶段 */
一般情况下,我们自定义的模块,大多数是挂载在
NGX_HTTP_CONTENT_PHASE阶段的。挂载的动作一般是在模块上下文调用的
postconfiguration 函数中。注意:有几个阶段是特例,它不调用挂载任何的
Handler,也就是你就不用挂载到这几个阶段了:
NGX_HTTP_FIND_CONFIG_PHASE
NGX_HTTP_POST_ACCESS_PHASE
NGX_HTTP_POST_REWRITE_PHASE
NGX_HTTP_TRY_FILES_PHASE
circle gif ngx_command_t looks like this:
{ ngx_string("circle_gif"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_circle_gif,
0,
0,
NULL }
挂载函数:
static char *
ngx_http_circle_gif(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_circle_gif_handler;
return NGX_CONF_OK;
}
typedef ngx_int_t (*ngx_http_output_header_filter_pt) (ngx_http_request_t *r);
typedef ngx_int_t (*ngx_http_output_body_filter_pt) (ngx_http_request_t *r, ngx_chain_t *chain);
其中,参数 r
是当前的请求
,chain
是待发送的 HTTP 响应包体
;
extern ngx_http_output_header_filter_pt ngx_http_top_header_filter;
extern ngx_http_output_body_filter_pt ngx_http_top_body_filter;
过滤模块链表中通过 next 遍历,其定义如下:
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
/* 发送 HTTP 响应头部 */
ngx_int_t
ngx_http_send_header(ngx_http_request_t *r)
{
if (r->header_sent) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"header already sent");
return NGX_ERROR;
}
if (r->err_status) {
r->headers_out.status = r->err_status;
r->headers_out.status_line.len = 0;
}
return ngx_http_top_header_filter(r);
}
/* 发送HTTP 响应包体 */
ngx_int_t
ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_int_t rc;
ngx_connection_t *c;
c = r->connection;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http output filter \"%V?%V\"", &r->uri, &r->args);
rc = ngx_http_top_body_filter(r, in);
if (rc == NGX_ERROR) {
/* NGX_ERROR may be returned by any filter */
c->error = 1;
}
return rc;
}
typedef struct ngx_chain_s ngx_chain_t;
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
struct ngx_buf_s {
u_char *pos; /* 当前buffer真实内容的起始位置 */
u_char *last; /* 当前buffer真实内容的结束位置 */
off_t file_pos; /* 在文件中真实内容的起始位置 */
off_t file_last; /* 在文件中真实内容的结束位置 */
u_char *start; /* buffer内存的开始分配的位置 */
u_char *end; /* buffer内存的结束分配的位置 */
ngx_buf_tag_t tag; /* buffer属于哪个模块的标志 */
ngx_file_t *file; /* buffer所引用的文件 */
/* 用来引用替换过后的buffer,以便当所有buffer输出以后,
* 这个影子buffer可以被释放。
*/
ngx_buf_t *shadow;
/* the buf's content could be changed */
unsigned temporary:1;
/*
* the buf's content is in a memory cache or in a read only memory
* and must not be changed
*/
unsigned memory:1;
/* the buf's content is mmap()ed and must not be changed */
unsigned mmap:1;
unsigned recycled:1; /* 内存可以被输出并回收 */
unsigned in_file:1; /* buffer的内容在文件中 */
/* 马上全部输出buffer的内容, gzip模块里面用得比较多 */
unsigned flush:1;
/* 基本上是一段输出链的最后一个buffer带的标志,标示可以输出,
* 有些零长度的buffer也可以置该标志
*/
unsigned sync:1;
/* 所有请求里面最后一块buffer,包含子请求 */
unsigned last_buf:1;
/* 当前请求输出链的最后一块buffer */
unsigned last_in_chain:1;
/* shadow链里面的最后buffer,可以释放buffer了 */
unsigned last_shadow:1;
/* 是否是暂存文件 */
unsigned temp_file:1;
/* 统计用,表示使用次数 */
/* STUB */ int num;
};
static
ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r)
{
time_t if_modified_since;
if_modified_since = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
r->headers_in.if_modified_since->value.len);
/* step 1: decide whether to operate */
if (if_modified_since != NGX_ERROR &&
if_modified_since == r->headers_out.last_modified_time) {
/* step 2: operate on the header */
r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
r->headers_out.content_type.len = 0;
ngx_http_clear_content_length(r);
ngx_http_clear_accept_ranges(r);
}
/* step 3: call the next filter */
return ngx_http_next_header_filter(r);
}
static ngx_int_t ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in);
以下是一个例子:
/*
* Let's take a simple example.
* Suppose we want to insert the text "" to the end of every request.
* First, we need to figure out if the response's final buffer is included in the buffer chain we were given.
* Like I said, there's not a fancy API, so we'll be rolling our own for loop:
*/
ngx_chain_t *chain_link;
int chain_contains_last_buffer = 0;
chain_link = in;
for ( ; ; ) {
if (chain_link->buf->last_buf)
chain_contains_last_buffer = 1;
if (chain_link->next == NULL)
break;
chain_link = chain_link->next;
}
/*
* Now let's bail out if we don't have that last buffer:
*/
if (!chain_contains_last_buffer)
return ngx_http_next_body_filter(r, in);
/*
* Super, now the last buffer is stored in chain_link.
* Now we allocate a new buffer:
*/
ngx_buf_t *b;
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
/*
* And put some data in it:
*/
b->pos = (u_char *) "";
b->last = b->pos + sizeof("") - 1;
/*
* And hook the buffer into a new chain link:
*/
ngx_chain_t *added_link;
added_link = ngx_alloc_chain_link(r->pool);
if (added_link == NULL)
return NGX_ERROR;
added_link->buf = b;
added_link->next = NULL;
/*
* Finally, hook the new chain link to the final chain link we found before:
*/
chain_link->next = added_link;
/*
* And reset the "last_buf" variables to reflect reality:
*/
chain_link->buf->last_buf = 0;
added_link->buf->last_buf = 1;
/*
* And pass along the modified chain to the next output filter:
*/
return ngx_http_next_body_filter(r, in);
/*
* The resulting function takes much more effort than what you'd do with, say, mod_perl ($response->body =~ s/$//),
* but the buffer chain is a very powerful construct, allowing programmers to process data incrementally so that the client gets something as soon as possible.
* However, in my opinion, the buffer chain desperately needs a cleaner interface so that programmers can't leave the chain in an inconsistent state.
* For now, manipulate it at your own risk.
*/
static ngx_http_module_t ngx_http_chunked_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_chunked_filter_init, /* postconfiguration */
...
};
其中
ngx_http_chunked_filter_init 处理如下定义:
static ngx_int_t
ngx_http_chunked_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_chunked_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_chunked_body_filter;
return NGX_OK;
}
由于
Filter 模块是 “
CHAIN OF RESPONSIBILITY” 链表模式的。
Handler 模块生成响应后,
Filter 模块调用两个函数:
ngx_http_output_filter 和
ngx_http_send_header,其中
ngx_http_output_filter 函数是调用全局函数
ngx_http_top_body_filter;
ngx_http_send_header 函数是调用全局函数
ngx_http_top_header_filter。
ngx_int_t
ngx_http_send_header(ngx_http_request_t *r)
{
...
return ngx_http_top_header_filter(r);
}
ngx_int_t
ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_int_t rc;
ngx_connection_t *c;
c = r->connection;
rc = ngx_http_top_body_filter(r, in);
if (rc == NGX_ERROR) {
/* NGX_ERROR may be returned by any filter */
c->error = 1;
}
return rc;
}
Filter 模块的执行方式如下图所示:
Filter 模块编写步骤如下
/*
* "config" for filter modules:
*/
ngx_addon_name=ngx_http__module /* 模块的名称 */
HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http__module" /* 保存所有 HTTP 模块*/
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http__module.c" /* 指定新模块的源码路径 */
/*
* "config" for other modules:
*/
ngx_addon_name=ngx_http__module
HTTP_MODULES="$HTTP_MODULES ngx_http__module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http__module.c"
#include
#include
#include
typedef struct
{
ngx_str_t hello_string;
ngx_int_t hello_counter;
}ngx_http_hello_loc_conf_t;
static ngx_int_t ngx_http_hello_init(ngx_conf_t *cf);
static void *ngx_http_hello_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_hello_commands[] = {
{
ngx_string("hello_string"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,
ngx_http_hello_string,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_hello_loc_conf_t, hello_string),
NULL },
{
ngx_string("hello_counter"),
NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_http_hello_counter,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_hello_loc_conf_t, hello_counter),
NULL },
ngx_null_command
};
/*
static u_char ngx_hello_default_string[] = "Default String: Hello, world!";
*/
static int ngx_hello_visited_times = 0;
static ngx_http_module_t ngx_http_hello_module_ctx = {
NULL, /* preconfiguration */
ngx_http_hello_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_hello_create_loc_conf, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_hello_module = {
NGX_MODULE_V1,
&ngx_http_hello_module_ctx, /* module context */
ngx_http_hello_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
};
static ngx_int_t
ngx_http_hello_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t out;
ngx_http_hello_loc_conf_t* my_conf;
u_char ngx_hello_string[1024] = {0};
ngx_uint_t content_length = 0;
ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "ngx_http_hello_handler is called!");
my_conf = ngx_http_get_module_loc_conf(r, ngx_http_hello_module);
if (my_conf->hello_string.len == 0 )
{
ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "hello_string is empty!");
return NGX_DECLINED;
}
if (my_conf->hello_counter == NGX_CONF_UNSET
|| my_conf->hello_counter == 0)
{
ngx_sprintf(ngx_hello_string, "%s", my_conf->hello_string.data);
}
else
{
ngx_sprintf(ngx_hello_string, "%s Visited Times:%d", my_conf->hello_string.data,
++ngx_hello_visited_times);
}
ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "hello_string:%s", ngx_hello_string);
content_length = ngx_strlen(ngx_hello_string);
/* we response to 'GET' and 'HEAD' requests only */
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
return NGX_HTTP_NOT_ALLOWED;
}
/* discard request body, since we don't need it here */
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
/* set the 'Content-type' header */
/*
*r->headers_out.content_type.len = sizeof("text/html") - 1;
*r->headers_out.content_type.data = (u_char *)"text/html";
*/
ngx_str_set(&r->headers_out.content_type, "text/html");
/* send the header only, if the request type is http 'HEAD' */
if (r->method == NGX_HTTP_HEAD) {
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = content_length;
return ngx_http_send_header(r);
}
/* allocate a buffer for your response body */
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/* attach this buffer to the buffer chain */
out.buf = b;
out.next = NULL;
/* adjust the pointers of the buffer */
b->pos = ngx_hello_string;
b->last = ngx_hello_string + content_length;
b->memory = 1; /* this buffer is in memory */
b->last_buf = 1; /* this is the last buffer in the buffer chain */
/* set the status line */
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = content_length;
/* send the headers of your response */
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
/* send the buffer chain of your response */
return ngx_http_output_filter(r, &out);
}
static void *ngx_http_hello_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_hello_loc_conf_t* local_conf = NULL;
local_conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_loc_conf_t));
if (local_conf == NULL)
{
return NULL;
}
ngx_str_null(&local_conf->hello_string);
local_conf->hello_counter = NGX_CONF_UNSET;
return local_conf;
}
/*
static char *ngx_http_hello_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_hello_loc_conf_t* prev = parent;
ngx_http_hello_loc_conf_t* conf = child;
ngx_conf_merge_str_value(conf->hello_string, prev->hello_string, ngx_hello_default_string);
ngx_conf_merge_value(conf->hello_counter, prev->hello_counter, 0);
return NGX_CONF_OK;
}*/
static char *
ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_hello_loc_conf_t* local_conf;
local_conf = conf;
char* rv = ngx_conf_set_str_slot(cf, cmd, conf);
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_string:%s", local_conf->hello_string.data);
return rv;
}
static char *ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_http_hello_loc_conf_t* local_conf;
local_conf = conf;
char* rv = NULL;
rv = ngx_conf_set_flag_slot(cf, cmd, conf);
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_counter:%d", local_conf->hello_counter);
return rv;
}
static ngx_int_t
ngx_http_hello_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_hello_handler;
return NGX_OK;
}
./configure --add-module=path/to/your/new/module/directory
make
make install