ngx_stream_echo_module
使用echo指令输出字符串。
repo地址:https://github.com/seawish/stream-echo-nginx-module。
该repo是从openresty处fork下来的。
Nginx版本
- nginx-1.10.3
源码方式安装nginx
参照博文Nginx源码编译安装教程
定义模块Context
1. 定义ngx_http_module_t类型的结构体变量
/**
* 定义ngx_stream_module_t类型的结构体变量 命名规则为ngx_http_[module-name]_module_ctx,这个结构主要用于定义各个Hook函数
*
* nginx1.11.02版本之前可以看到一共有6个Hook注入点,分别会在不同时刻被Nginx调用,由于我们的模块仅仅用于server域,这里将不需要的注入点设为NULL即可。nginx1.11.02+的版本增加了preconfiguration注入点。
*
* ngx_stream_echo_create_srv_conf ngx_stream_echo_merge_srv_conf 这两个函数会被Nginx自动调用。注意这里的命名规则:ngx_http_[module-name]_[create|merge]_[main|srv|loc]_conf。
*/
static ngx_stream_module_t ngx_stream_echo_module_ctx = {
#if (nginx_version >= 1011002)
NULL, /* preconfiguration */
#endif
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
ngx_stream_echo_create_srv_conf, /* create server configuration */
ngx_stream_echo_merge_srv_conf /* merge server configuration */
};
2. 初始化一个配置结构体
typedef struct {
ngx_array_t cmds; /* of elements of type ngx_stream_echo_cmd_t */
ngx_msec_t send_timeout;
ngx_msec_t read_timeout;
ngx_uint_t log_level;
ngx_uint_t lingering_close;
ngx_msec_t lingering_time;
ngx_msec_t lingering_timeout;
size_t read_buffer_size;
unsigned needs_buffer_in; /* :1 */
} ngx_stream_echo_srv_conf_t;
3. 实现了配置的继承
将其parent block的配置信息合并到此结构体。 Nginx 为不同的数据类型提供了merge 函数,可查阅 core/ngx_conf_file.h;merge_loc_conf 函数定义如下:
static char *
ngx_stream_echo_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_stream_echo_srv_conf_t *prev = parent;
ngx_stream_echo_srv_conf_t *conf = child;
#if 0
if (conf->cmds.nelts == 0 && prev->cmds.nelts > 0) {
/* assuming that these arrays are read-only afterwards */
ngx_memcpy(&conf->cmds, &prev->cmds, sizeof(ngx_array_t));
}
#endif
ngx_conf_merge_str_value(conf->ed, prev->ed, '"');
return NGX_CONF_OK;
}
定义echo模块的指令和参数转化函数
定义echo模块的指令
/**
* 定义echo模块的指令。
* ngx_command_t在https://github.com/nginx/nginx/blob/master/src/core/ngx_conf_file.h定义。
*
*/
static ngx_command_t ngx_stream_echo_commands[] = {
{ ngx_string("echo"), /* echo命令 */
NGX_STREAM_SRV_CONF|NGX_CONF_ANY,
ngx_stream_echo_echo,
NGX_STREAM_SRV_CONF_OFFSET,
0,
NULL },
ngx_null_command /* 空命令 */
};
参数转化函数
ngx_stream_echo_helper
static ngx_stream_echo_cmd_t *
ngx_stream_echo_helper(ngx_conf_t *cf, ngx_command_t *cmd, void *conf,
ngx_stream_echo_opcode_t opcode, ngx_array_t *args, ngx_array_t *opts)
{
ngx_stream_echo_srv_conf_t *escf = conf;
ngx_stream_echo_cmd_t *echo_cmd;
ngx_stream_core_srv_conf_t *cscf;
if (escf->cmds.nelts == 0) {
cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module);
cscf->handler = ngx_stream_echo_handler;
}
echo_cmd = ngx_array_push(&escf->cmds);
if (echo_cmd == NULL) {
return NULL;
}
echo_cmd->opcode = opcode;
if (args != NULL && opts != NULL) {
if (ngx_array_init(args, cf->temp_pool, cf->args->nelts - 1,
sizeof(ngx_str_t))
== NGX_ERROR)
{
return NULL;
}
if (ngx_array_init(opts, cf->temp_pool, 1, sizeof(ngx_str_t))
== NGX_ERROR)
{
return NULL;
}
if (ngx_stream_echo_eval_args(cf->args, 1, args, opts) != NGX_OK) {
return NULL;
}
}
return echo_cmd;
}
ngx_stream_echo_echo
static char *
ngx_stream_echo_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
u_char *p;
size_t size;
unsigned nl; /* controls whether to append a newline char */
ngx_str_t *opt, *arg;
ngx_uint_t i;
ngx_array_t opts, args;
ngx_stream_echo_cmd_t *echo_cmd;
echo_cmd = ngx_stream_echo_helper(cf, cmd, conf,
NGX_STREAM_ECHO_OPCODE_ECHO,
&args, &opts);
if (echo_cmd == NULL) {
return NGX_CONF_ERROR;
}
/* handle options */
nl = 1;
opt = opts.elts;
for (i = 0; i < opts.nelts; i++) {
if (opt[i].len == 1 && opt[i].data[0] == 'n') {
nl = 0;
continue;
}
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"stream echo sees unknown option \"-%*s\" "
"in \"echo\"", opt[i].len, opt[i].data);
return NGX_CONF_ERROR;
}
/* prepare the data buffer to be sent.
* TODO we could merge the data buffers of adjacent "echo" commands
* further though it might not worth the trouble. oh well.
*/
/* step 1: pre-calculate the total size of the data buffer actually
* needed and allocate the buffer. */
arg = args.elts;
for (size = 0, i = 0; i < args.nelts; i++) {
if (i > 0) {
/* preserve a byte for prepending a space char */
size++;
}
size += arg[i].len;
}
if (nl) {
/* preserve a byte for the trailing newline char */
size++;
}
if (size == 0) {
echo_cmd->data.buffer.data = NULL;
echo_cmd->data.buffer.len = 0;
return NGX_CONF_OK;
}
p = ngx_palloc(cf->pool, size);
if (p == NULL) {
return NGX_CONF_ERROR;
}
echo_cmd->data.buffer.data = p;
echo_cmd->data.buffer.len = size;
/* step 2: fill in the buffer with actual data */
for (i = 0; i < args.nelts; i++) {
if (i > 0) {
/* prepending a space char */
*p++ = (u_char) ' ';
}
p = ngx_copy(p, arg[i].data, arg[i].len);
}
if (nl) {
/* preserve a byte for the trailing newline char */
*p++ = LF;
}
if (p - echo_cmd->data.buffer.data != (off_t) size) {
/* just as an insurance */
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"stream echo internal buffer error: %O != %uz",
p - echo_cmd->data.buffer.data, size);
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
编写Handler 模块
在ngx_stream_echo_helper方法中修改核心模块配置(也就是当前service), 将其handler替换为我们自己定义的ngx_stream_echo_handler。
static void
ngx_stream_echo_handler(ngx_stream_session_t *s)
{
ngx_connection_t *c;
ngx_stream_echo_ctx_t *ctx;
ngx_stream_echo_srv_conf_t *escf;
escf = ngx_stream_get_module_srv_conf(s, ngx_stream_echo_module);
if (escf->cmds.nelts == 0) {
/* cannot really happen */
ngx_stream_echo_finalize(s, NGX_DECLINED);
return;
}
c = s->connection;
c->write->handler = ngx_stream_echo_writer;
c->read->handler = ngx_stream_echo_block_reading;
ctx = ngx_stream_echo_create_ctx(s);
if (ctx == NULL) {
ngx_stream_echo_finalize(s, NGX_ERROR);
return;
}
ngx_stream_set_ctx(s, ctx, ngx_stream_echo_module);
ngx_stream_echo_resume_execution(s);
}
组合Nginx Module
/**
* 组合Nginx Module
*
* 上面完成了Nginx模块各种组件的开发,下面就是将这些组合起来了。一个Nginx模块被定义为一个ngx_module_t结构体https://github.com/nginx/nginx/blob/master/src/core/ngx_module.h,这个结构体的字段很多,不过开头和结尾若干字段一般可以通过Nginx内置的宏去填充
*
* 开头和结尾分别用NGX_MODULE_V1和NGX_MODULE_V1_PADDING 填充了若干字段,就不去深究了。
* 这里主要需要填入的信息从上到下以依次为context、指令数组、模块类型以及若干特定事件的回调处理函数(不需要可以置为NULL),
* 其中内容还是比较好理解的,注意我们的echo是一个STREAM模块,所以这里类型是NGX_STREAM_MODULE,其它可用类型还有NGX_EVENT_MODULE(事件处理模块)和NGX_MAIL_MODULE(邮件模块),NGX_STREAM_MODULE等。
*
*/
ngx_module_t ngx_stream_echo_module = {
NGX_MODULE_V1,
&ngx_stream_echo_module_ctx, /* module context */
ngx_stream_echo_commands, /* module directives */
NGX_STREAM_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
};
编写config文件
ngx_addon_name=ngx_stream_echo_module
STREAM_MODULES="$STREAM_MODULES ngx_stream_echo_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_stream_echo_module.c"
NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ddebug.h"
编译安装echo模块
./configure --prefix=/opt/nginx \
--with-stream --with-debug \
--add-module=/path/to/stream-echo-nginx-module
例如:
./configure --prefix=/opt/nginx \
--with-stream --with-debug \
--add-module=/Users/zsb/Documents/Workspaces/nginx-dev/nginx-moudle/stream-echo-nginx-module
指令
echo
syntax: echo [options] <string>...
default: no
context: server
phase: content
Sends string arguments joined by spaces, along with a trailing newline, out to the client.
For example,
stream {
server {
listen 1234;
echo "Hello, world!";
echo foo bar baz;
}
}
Then connecting to the server port 1234 will immediately receive the response data
Hello, world!
foo bar baz
参考文献
本文作者: seawish
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!