配色: 字号:
手把手教你开发Nginx模块
2016-11-10 | 阅:  转:  |  分享 
  
手把手教你开发Nginx模块



前面的哪些话



关于Nginx模块开发的博客资料,网上很多,很多。但是,每篇博客都只提要点,无法"stepbystep"照着做,对于初次接触Nginx开发的同学,只能像只盲目的蚂蚁瞎燥急!该篇文章没有太多技术深度,只是一步一步说明白Nginx模块的开发过程。



开发环境搭建



工欲善其事,必先利其器。个人推荐EclipseCDT作为IDE,原因很简单,代码提示与补全功能很全,完胜Codeblock这类...相信与否,试过就知道。



在ubuntu下搭建开发环境:



安装GCC编译器



apt-getinstallbuild-essential

安装pcre/openssl/zlib开发库



apt-getinstalllibpcre3-dev

apt-getinstalllibssl-dev

apt-getinstalllibzip-dev

必需安装nginx核心模块依赖的pcre,openssl,zilib开发库

安装JRE/EclipseCDT



apt-getinstallopenjdk-8-jre

wgethttp://ftp.yz.yamagata-u.ac.jp/pub/eclipse//technology/epp/downloads/release/neon/R/eclipse-cpp-neon-R-linux-gtk-x86_64.tar.gz&&tzr-xzvfeclipse-cpp-neon-R-linux-gtk-x86_64.tar.gz

下载nginx源码



wgethttp://nginx.org/download/nginx-1.10.1.tar.gz&&tar-xzvfnginx-1.10.1.tar.gz

配置CDTBuildEnvironment

添加变量,值Nginxsrc下各模块路径,用冒号分隔,例如:



/root/Workspace/nginx-1.10.1/src/core:/root/Workspace/nginx-1.10.1/src/event:/root/Workspace/nginx-1.10.1/src/http:/root/Workspace/nginx-1.10.1/src/mail:/root/Workspace/nginx-1.10.1/src/stream:/root/Workspace/nginx-1.10.1/src/os/unix

添加环境变量,创建C项目时自动作为-I选项

image

image



Nginx模块编译流程



Nginx使用configure脚本分析环境,自动生成objs结果。哪么configure如何编译第三方模块?答案是--add-module指定第三方模块目录,并将目录存为$ngx_addon_dir环境变量。执行$ngx_addon_dir/config脚本,读取模块配置。在config中的环境变量分为2种:小写的本地环境变量,大写的全局环境变量。例如:



ngx_addon_name=ngx_http_mytest_module

HTTP_MODULES="$HTTP_MODULESngx_http_mytest_module"

NGX_ADDON_SRCS="$NGX_ADDON_SRCS$ngx_addon_dir/ngx_http_mytest_moudle.c"

CORE_LIBS="$CORE_LIBS-lpcre"

HTTP_MODULES中的ngx_http_mytest_module就是NGX_ADDON_SRCS中源码(如果有多个,都要写上)ngx_http_mytest_module.c中定义的ngx_module_t类型的全局变量。

可见,第三方模块的入口点就是ngx_module_t类型全局变量,该变量又关联ngx_http_module_t类型static变量,与ngx_command_t类型static数组。

在ngx_http_module_t中定义上下文配置nginx.conf解析的回调方法。

在ngx_command_t中定义配置项处理的set回调方法。

Nginx的全部操作都是异步的。在上述的方法中根据需要又会使用其他handler方法。

以上可以看成Nginx第三方模块的起式。

Upstream例子源码



config



ngx_addon_name=ngx_http_mytest_module

HTTP_MODULES="$HTTP_MODULESngx_http_mytest_module"

NGX_ADDON_SRCS="$NGX_ADDON_SRCS$ngx_addon_dir/ngx_http_mytest_module.c"

源代码



#include

#include

#include

#include



typedefstruct{

ngx_http_upstream_conf_tupstream;

}mytest_conf_t;



typedefstruct{

ngx_http_status_tstatus;

ngx_str_tbackendServer;

}mytest_ctx_t;



staticvoidmytest_create_loc_conf(ngx_conf_tcf);

staticcharmytest_merge_loc_conf(ngx_conf_tcf,voidparent,voidchild);

staticngx_int_tmytest_upstream_create_request(ngx_http_request_tr);

staticngx_int_tmytest_upstream_process_status_line(ngx_http_request_tr);

staticngx_int_tmytest_upstream_process_header(ngx_http_request_tr);

staticvoidmytest_upstream_finalize_request(ngx_http_request_tr,

ngx_int_trc);

staticngx_int_tmytest_handler(ngx_http_request_tr);

staticcharmytest(ngx_conf_tcf,ngx_command_tcmd,voidconf);





staticngx_http_module_tmytest_ctx={

NULL,

NULL,

NULL,

NULL,

NULL,

NULL,

mytest_create_loc_conf,

mytest_merge_loc_conf

};



staticngx_command_tmytest_commands[]={

{

ngx_string("mytest"),

NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,

mytest,

NGX_HTTP_LOC_CONF_OFFSET,

0,

NULL

},

ngx_null_command

};



ngx_module_tngx_http_mytest_module={

NGX_MODULE_V1,

&mytest_ctx,

mytest_commands,

NGX_HTTP_MODULE,

NULL,

NULL,

NULL,

NULL,

NULL,

NULL,

NULL,

NGX_MODULE_V1_PADDING

};



staticngx_str_tmytest_upstream_hide_headers[]=

{

ngx_string("Date"),

ngx_string("Server"),

ngx_string("X-Pad"),

ngx_string("X-Accel-Expires"),

ngx_string("X-Accel-Redirect"),

ngx_string("X-Accel-Limit-Rate"),

ngx_string("X-Accel-Buffering"),

ngx_string("X-Accel-Charset"),

ngx_null_string

};



staticvoidmytest_create_loc_conf(ngx_conf_tcf){

mytest_conf_tmycf;

mycf=(mytest_conf_t)ngx_pcalloc(cf->pool,sizeof(mytest_conf_t));

if(mycf==NULL){

returnNULL;

}



mycf->upstream.connect_timeout=60000;

mycf->upstream.send_timeout=60000;

mycf->upstream.read_timeout=60000;

mycf->upstream.store_access=0600;



mycf->upstream.buffering=0;

mycf->upstream.bufs.num=8;

mycf->upstream.bufs.size=ngx_pagesize;

mycf->upstream.buffer_size=ngx_pagesize;

mycf->upstream.busy_buffers_size=2ngx_pagesize;

mycf->upstream.temp_file_write_size=2ngx_pagesize;

mycf->upstream.max_temp_file_size=102410241024;



mycf->upstream.hide_headers=NGX_CONF_UNSET_PTR;

mycf->upstream.pass_headers=NGX_CONF_UNSET_PTR;



returnmycf;

}



staticcharmytest_merge_loc_conf(ngx_conf_tcf,voidparent,voidchild){

mytest_conf_tprev=(mytest_conf_t)parent;

mytest_conf_tconf=(mytest_conf_t)child;



ngx_hash_init_thash;

hash.max_size=100;

hash.bucket_size=1024;

hash.name="proxy_headers_hash";

if(ngx_http_upstream_hide_headers_hash(cf,&conf->upstream,&prev->upstream,mytest_upstream_hide_headers,&hash)!=NGX_OK){

returnNGX_CONF_ERROR;

}

returnNGX_CONF_OK;

}



staticngx_int_tmytest_upstream_create_request(ngx_http_request_tr){

staticngx_str_tbackendQueryLine=ngx_string("GET/search?q=%VHTTP/1.1\r\nHost:www.google.com.hk\r\nConnection:close\r\n\r\n");

ngx_int_tqueryLineLen=backendQueryLine.len+r->args.len-2;



ngx_buf_tb=ngx_create_temp_buf(r->pool,queryLineLen);

if(b==NULL)returnNGX_ERROR;

b->last=b->pos+queryLineLen;



ngx_snprintf(b->pos,queryLineLen,(char)backendQueryLine.data,&r->args);



r->upstream->request_bufs=ngx_alloc_chain_link(r->pool);

if(r->upstream->request_bufs==NULL)returnNGX_ERROR;



r->upstream->request_bufs->buf=b;

r->upstream->request_bufs->next=NULL;



r->upstream->request_sent=0;

r->upstream->header_sent=0;



r->header_hash=1;



returnNGX_OK;

}



staticngx_int_tmytest_upstream_process_status_line(ngx_http_request_tr){

size_tlen;

ngx_int_trc;

ngx_http_upstream_tu;



mytest_ctx_tctx=ngx_http_get_module_ctx(r,ngx_http_mytest_module);

if(ctx==NULL)returnNGX_ERROR;



u=r->upstream;



rc=ngx_http_parse_status_line(r,&u->buffer,&ctx->status);

if(rc==NGX_AGAIN)returnrc;

if(rc==NGX_ERROR){

ngx_log_error(NGX_LOG_ERR,r->connection->log,0,"upstreamsenttovalidHTTP/1.0header");



r->http_version=NGX_HTTP_VERSION_9;

u->state->status=NGX_HTTP_OK;



returnNGX_OK;

}



if(u->state){

u->state->status=ctx->status.code;

}



u->headers_in.status_n=ctx->status.code;

len=ctx->status.end-ctx->status.start;

u->headers_in.status_line.len=len;

u->headers_in.status_line.data=ngx_pcalloc(r->pool,len);

if(u->headers_in.status_line.data==NULL)returnNGX_ERROR;



ngx_memcpy(u->headers_in.status_line.data,ctx->status.start,len);



u->process_header=mytest_upstream_process_header;



returnmytest_upstream_process_header(r);

}



staticngx_int_tmytest_upstream_process_header(ngx_http_request_tr){

ngx_int_trc;

ngx_table_elt_th;

ngx_http_upstream_header_thh;

ngx_http_upstream_main_conf_tumcf;



umcf=ngx_http_get_module_main_conf(r,ngx_http_upstream_module);



for(;;){

rc=ngx_http_parse_header_line(r,&r->upstream->buffer,1);

if(rc==NGX_OK){

h=ngx_list_push(&r->upstream->headers_in.headers);

if(h==NULL)returnNGX_ERROR;



h->hash=r->header_hash;

h->key.len=r->header_name_end-r->header_name_start;

h->value.len=r->header_end-r->header_start;



h->key.data=ngx_pcalloc(r->pool,h->key.len+1+h->value.len+1+h->key.len);

if(h->key.data==NULL)returnNGX_ERROR;



h->value.data=h->key.data+h->key.len+1;

h->lowcase_key=h->key.data+h->key.len+1+h->value.len+1;



ngx_memcpy(h->key.data,r->header_name_start,h->key.len);

h->key.data[h->key.len]=''\0'';

ngx_memcpy(h->value.data,r->header_start,h->value.len);

h->value.data[h->value.len]=''\0'';



if(h->key.len==r->lowcase_index){

ngx_memcpy(h->lowcase_key,r->lowcase_header,h->key.len);

}else{

ngx_strlow(h->lowcase_key,h->key.data,h->key.len);

}



hh=ngx_hash_find(&umcf->headers_in_hash,h->hash,h->lowcase_key,h->key.len);

if(hh&&hh->handler(r,h,hh->offset)!=NGX_OK)returnNGX_ERROR;



continue;

}



if(rc==NGX_HTTP_PARSE_HEADER_DONE){

if(r->upstream->headers_in.server==NULL){

h=ngx_list_push(&r->upstream->headers_in.headers);

if(h==NULL)returnNGX_ERROR;

h->hash=ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(''s'',''e''),''r''),''v''),''e''),''r'');

ngx_str_set(&h->key,"Server");

ngx_str_null(&h->value);

h->lowcase_key=(u_char)"server";

}



if(r->upstream->headers_in.date==NULL){

h=ngx_list_push(&r->upstream->headers_in.headers);

if(h==NULL)returnNGX_ERROR;

h->hash=ngx_hash(ngx_hash(ngx_hash(''d'',''a''),''t''),''e'');

ngx_str_set(&h->key,"Date");

ngx_str_null(&h->value);

h->lowcase_key=(u_char)"date";

}

returnNGX_OK;

}

if(rc==NGX_AGAIN)returnNGX_AGAIN;

ngx_log_error(NGX_LOG_ERR,r->connection->log,0,"upstreamsentinvalidheader");



returnNGX_HTTP_UPSTREAM_FT_INVALID_HEADER;

}

}



staticvoidmytest_upstream_finalize_request(ngx_http_request_tr,ngx_int_trc){

ngx_log_error(NGX_LOG_DEBUG,r->connection->log,0,"mytest_upstream_finalize_request");

}



staticcharmytest(ngx_conf_tcf,ngx_command_tcmd,voidconf){

ngx_http_core_loc_conf_tclcf;



clcf=ngx_http_conf_get_module_loc_conf(cf,ngx_http_core_module);

clcf->handler=mytest_handler;



returnNGX_CONF_OK;

}



staticngx_int_tmytest_handler(ngx_http_request_tr){

mytest_ctx_tmyctx=ngx_http_get_module_ctx(r,ngx_http_mytest_module);

if(myctx==NULL){

myctx=ngx_pcalloc(r->pool,sizeof(mytest_ctx_t));

if(myctx==NULL)returnNGX_ERROR;

ngx_http_set_ctx(r,myctx,ngx_http_mytest_module);

}



if(ngx_http_upstream_create(r)!=NGX_OK){

ngx_log_error(NGX_LOG_ERR,r->connection->log,0,"ngx_http_upstream_create()failed");

returnNGX_ERROR;

}



mytest_conf_tmycf=(mytest_conf_t)ngx_http_get_module_loc_conf(r,ngx_http_mytest_module);

ngx_http_upstream_tu=r->upstream;

u->conf=&mycf->www.wang027.comupstream;

u->buffering=mycf->upstream.buffering;



u->resolved=(ngx_http_upstream_resolved_t)ngx_pcalloc(r->pool,sizeof(ngx_http_upstream_resolved_t));

if(u->resolved==NULL){

ngx_log_error(NGX_LOG_ERR,r->connection->log,0,"ngx_pcallocresolvederror.%s",strerror(errno));

returnNGX_ERROR;

}



staticstructsockaddr_inbackendSockAddr;



structhostentpHost=gethostbyname((char)"www.google.com.hk");

if(pHost==NULL){

ngx_log_error(NGX_LOG_ERR,r->connection->log,0,"gethostbynamefail.%s",strerror(errno));

returnNGX_ERROR;

}



backendSockAddr.sin_family=AF_INET;

backendSockAddr.sin_port=htons((in_port_t)80);

charpDmsIP=inet_ntoa((structin_addr)(pHost->h_addr_list[0]));

backendSockAddr.sin_addr.s_addr=inet_addr(pDmsIP);



myctx->backendServer.data=(u_char)pDmsIP;

myctx->backendServer.len=strlen(pDmsIP);



u->resolved->sockaddr=(structsockaddr)&backendSockAddr;

u->resolved->port=htons((in_port_t)80);

u->resolved->socklen=sizeof(structsockaddr_in);

u->resolved->naddrs=1;



u->create_request=mytest_upstream_create_request;

u->process_header=mytest_upstream_process_status_line;

u->finalize_request=mytest_upstream_finalize_request;



r->main->count++;



ngx_http_upstream_init(r);

returnNGX_DONE;

}

注意:《Nginx深入解析》的demo少了这句:“u->resolved->port=htons((in_port_t)80);”,否则报错“2016/09/0911:24:18[error]28352#0:1noportinupstream"",client:127.0.0.1,server:localhost,request:"GET/mytest?q=testHTTP/1.1",host:"localhost"”



编译脚本



./configure--prefix=/usr/local/nginx--add-module=/root/Workspace/nginx-modules/ngx_http_mytest_module--with-debug

sudomake

sudomakeinstall

安装后即可到/usr/local/nginx下配置nginx.conf进行测试。



Subrequest例子源码



config



ngx_addon_name=ngx_http_mytest_module

HTTP_MODULES="$HTTP_MODULESngx_http_mytest_module"

NGX_ADDON_SRCS="$NGX_ADDON_SRCS$ngx_addon_dir/ngx_http_mytest_module.c"

源代码



#include

#include

#include



typedefstruct{

ngx_str_tstock[6];

}mytest_ctx_t;



staticngx_int_tmytest_subrequest_post_handler(ngx_http_request_tr,voiddata,ngx_int_trc);

staticvoidmytest_post_handler(ngx_http_request_tr);

staticngx_int_tmytest_handler(ngx_http_request_tr);

staticcharmytest(ngx_conf_tcf,ngx_command_tcmd,voidconf);



staticngx_http_module_tmytest_conf={

NULL,

NULL,

NULL,

NULL,

NULL,

NULL,

NULL,

NULL

};

staticngx_command_tmytest_commands[]={

{

ngx_string("mytest"),

NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,

mytest,

NGX_HTTP_LOC_CONF_OFFSET,

0,

NULL

},

ngx_null_command

};



ngx_module_tngx_http_mytest_module={

NGX_MODULE_V1,

&mytest_conf,

mytest_commands,

NGX_HTTP_MODULE,

NULL,

NULL,

NULL,

NULL,

NULL,

NULL,

NULL,

NGX_MODULE_V1_PADDING

};



staticngx_int_tmytest_subrequest_post_handler(ngx_http_request_tr,

voiddata,ngx_int_trc){

ngx_http_request_tpr=r->parent;



mytest_ctx_tmyctx=ngx_http_get_module_ctx(pr,ngx_http_mytest_module);

pr->headers_out.status=r->headers_out.status;

if(r->headers_out.status==NGX_HTTP_OK){

intflag=0;

ngx_buf_tpRecvBuf=&r->upstream->buffer;

for(;pRecvBuf->pos!=pRecvBuf->last;pRecvBuf->pos++){

if(pRecvBuf->pos=='',''||pRecvBuf->pos==''\"''){

if(flag>0){

myctx->stock[flag-1].len=pRecvBuf->pos

-myctx->stock[flag-1].data;

}

flag++;

myctx->stock[flag-1].data=pRecvBuf->pos+1;

}

if(flag>6)

break;

}

}

pr->write_event_handler=mytest_post_handler;



returnNGX_OK;



}



staticvoidmytest_post_handler(ngx_http_request_tr){

if(r->headers_out.status!=NGX_HTTP_OK){

ngx_http_finalize_request(r,r->headers_out.status);

return;

}



mytest_ctx_tmyctx=ngx_http_get_module_ctx(r,ngx_http_mytest_module);

ngx_str_toutput_format=ngx_string("stock[%V],Todaycurrentprice:%V,volumn:%V");

intbodylen=output_format.len+myctx->stock[0].len+myctx->stock[1].len+myctx->stock[4].len-6;

r->headers_out.content_www.baiyuewang.netlength_n=bodylen;



ngx_buf_tb=ngx_create_temp_buf(r->pool,bodylen);

ngx_snprintf(b->pos,bodylen,(char)output_format.data,&myctx->stock[0],&myctx->stock[1],&myctx->stock[4]);

b->last=b->pos+bodylen;

b->last_buf=1;



ngx_chain_tout;

out.buf=b;

out.next=NULL;



staticngx_str_ttype=ngx_string("text/plain;charset=GBK");

r->headers_out.content_type=type;

r->headers_out.status=NGX_HTTP_OK;



r->connection->buffered|=NGX_HTTP_WRITE_BUFFERED;

ngx_int_tret=ngx_http_send_header(r);

ret=ngx_http_output_filter(r,&out);



ngx_http_finalize_request(r,ret);

}



staticngx_int_tmytest_handler(ngx_http_request_tr){

mytest_ctx_tmyctx=ngx_http_get_module_ctx(r,ngx_http_mytest_module);

if(myctx==NULL){

myctx=ngx_pcalloc(r->pool,sizeof(mytest_ctx_t));

if(myctx==NULL)returnNGX_ERROR;

ngx_http_set_ctx(r,myctx,ngx_http_mytest_module);

}



ngx_http_post_subrequest_tpsr=ngx_pcalloc(r->pool,sizeof(ngx_http_post_subrequest_t));

if(psr==NULL)returnNGX_HTTP_INTERNAL_SERVER_ERROR;



psr->handler=mytest_subrequest_post_handler;

psr->data=myctx;



ngx_str_tsub_prefix=ngx_string("/list=");

ngx_str_tsub_location;

sub_location.len=sub_prefix.len+r->args.len;

sub_location.data=ngx_pcalloc(r->pool,sub_location.len);

ngx_snprintf(sub_location.data,sub_location.len,"%V%V",&sub_prefix,&r->args);



ngx_http_request_tsr;

ngx_int_trc=ngx_http_subrequest(r,&sub_location,NULL,&sr,psr,NGX_HTTP_SUBREQUEST_IN_MEMORY);



if(rc!=NGX_OK)returnNGX_ERROR;



returnNGX_DONE;

}



staticcharmytest(ngx_conf_tcf,ngx_command_tcmd,voidconf){

ngx_http_core_loc_conf_tclcf=ngx_http_conf_get_module_loc_conf(cf,ngx_http_core_module);

clcf->handler=mytest_handler;

returnNGX_CONF_OK;

}

编译脚本



./configure--prefix=/usr/local/nginx--add-module=/root/Workspace/nginx-modules/ngx_http_mytest_module2--with-debug

sudomake

sudomakeinstall

献花(0)
+1
(本文系thedust79首藏)