前两篇文章看了后相信可以写出五花八门的有用的Nginx插件了。我写的都是一些基本的流程,Nginx提供的函数可以很直观地从函数名上表现出来,所以也没过多的说明。这一章说说Nginx的配置,主要是配置模块命令的参数,顺便提一下HTTP的参数处理。
本文链接
这次实现一个提供命令 “pw” 的模块,这个命令需要一个参数:
static ngx_command_t ngx_http_rw_commands[] = { { ngx_string("rw"), NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, //需要1个参数 ngx_http_rw_setup, 0, 0, NULL }, ngx_null_command };
模块命令调用的参数在这个command的set函数被内核调用的时候可以取得,即是内核传过来的ngx_conf_t变量里可以取得,cf->args->elts是一个参数字符串数组。
这时取到的参数我们可以存起来,也可以根据参数的不同指定不同的处理函数,本文的例子是用的第二种方式。
char* ngx_http_rw_setup(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); ngx_str_t* args; args=cf->args->elts; //根据参数的不同指定不同的处理函数。 if(ngx_strcmp("1",args[1].data)==0){ //如果参数是1 clcf->handler = ngx_http_pw1_handler; } else if(ngx_strcmp("2",args[1].data)==0){ //如果参数是2 clcf->handler = ngx_http_pw2_handler; } else{//其它参数 clcf->handler = ngx_http_pw3_handler; } return NGX_CONF_OK; }
接下来去实现这三个处理函数(具体实现看后面的整个代码):
static ngx_int_t ngx_http_pw1_handler(ngx_http_request_t *r){ //命令参数是1的时候执行此函数,我们在这儿简单地输出“you called action1” … } static ngx_int_t ngx_http_pw2_handler(ngx_http_request_t *r){ //命令参数是2的时候执行函数,我们在这儿简单地输出“you called action2” … } static ngx_int_t ngx_http_pw3_handler(ngx_http_request_t *r){ //其它命令执行此函数,这个函数会接受GET参数 … }
生成模块后,按如下方式配置Nginx
location /rw1{ rw 1; } location /rw2{ rw 2; } location /rw3{ rw 3; }
访问时按不同的路径访问就会有不同的结果。
上面的配置可以看出来,每个参数都需要一个location去配置,十分麻烦,比较好的办法是用get方式传递一个动作值,根据这个值去判断实现什么功能,然后在一个location下通过URL重写功能把匹配的命令转换成相应的GET请求。
URL重写功能现今主要用在搜索引擎优化这方面,其实它也可以防止SQL注入等不安全因素的出现,甚至还可以减少你判断参数合法性的工作!比如你某个参数必须接收整数,如果没有URL重写功能,你需要去判断它是不是整数,但如果有URL重写功能,不是整数的参数不符合重写规则自然也不会调用到你的脚本。
我在ngx_http_pw3_handler中实现了GET参数的接收,使其可以输出接收到的“action”变量(我们还可以根据不同的action做不同的处理)。实现功能之后,再在location /的配置下加上一个重写规则,就实现了和上面同样的功能,但这样却简化了配置,如果有成千上万个action,也不必去手工加配置。
location / { root html; index index.html index.htm; rewrite ^/action(\d)$ /rw3?action=$1; }
上面就是新加的重写规则,访问 /action1 /action2 /action3会给rw3传递不同的3个值,我们可以在rw3中根据值的不同进行不同的处理。
最后是整个代码:
/* * Nginx module ABC * by Hoverlees http://www.hoverlees.com */ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> char output1[]="you called action1"; char output2[]="you called action2"; char output3[]="<h1>you are calling action3:</h1>"; char noaction[]="No Action!"; char* ngx_http_rw_setup(ngx_conf_t *cf, ngx_command_t *cmd,void *conf); //这是一个简单的从args中提取参数的函数,Nginx内部我还没找到它提供的方式,这儿先随便写一个 //这个函数写得很粗糙,注意它不会判断缓冲区的大小! int hover_get_arg(const char* str,int slen,const char* aname,char* out,int* outLen){ int i,j,k; int keyLen=strlen(aname); char temp; for(i=0;i<slen;i++){ for(j=0;j<keyLen;j++){ if(str[i+j]!=aname[j]) break; } if(j==keyLen){ if(str[i+j]=='='){ j++; k=0; i=i+j; while(i<slen){ temp=str[i]; if(temp==0) break; if(temp=='&') break; if(out!=NULL) out[k]=temp; i++; k++; } out[k]=0; if(outLen!=NULL) *outLen=k; return 1; } } } return 0; } static ngx_command_t ngx_http_rw_commands[] = { { ngx_string("rw"), NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_rw_setup, 0, 0, NULL }, ngx_null_command }; static ngx_http_module_t ngx_http_rw_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_module_t ngx_http_rw_module = { NGX_MODULE_V1, &ngx_http_rw_module_ctx, /* module context */ ngx_http_rw_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_pw1_handler(ngx_http_request_t *r){ ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out; b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } out.buf = b; out.next = NULL; b->pos = output1; b->last = output1 + sizeof(output1) - 1; b->memory = 1; b->last_buf = 1; r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = sizeof(output1) - 1; rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } return ngx_http_output_filter(r, &out); } //也没什么好提的 static ngx_int_t ngx_http_pw2_handler(ngx_http_request_t *r){ ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out; b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } out.buf = b; out.next = NULL; b->pos = output2; b->last = output2 + sizeof(output2) - 1; b->memory = 1; b->last_buf = 1; r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = sizeof(output2) - 1; rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } return ngx_http_output_filter(r, &out); } //处理命令 pw 3 //接受action参数,并输出 static ngx_int_t ngx_http_pw3_handler(ngx_http_request_t *r){ ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out; ngx_chain_t out2; int contentLen=0; //这儿我用了两块内存放到输出链上,第一块链输出一个字符串,第二块链输出action的值。 ////////////////first buf b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } out.buf = b; out.next = NULL; b->pos = output3; b->last = output3 + sizeof(output3) - 1; b->memory = 1; b->last_buf = 1; contentLen+=sizeof(output3)-1; //////////////second buf char action[32]; int actionLen; b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } out2.buf = b; out2.next = NULL; out.next=&out2; if(hover_get_arg(r->args.data,r->args.len,"action",action,&actionLen)){//如果有action参数,输出action,但我们可以在这儿判断action的内容,然后根据不同的值做不同的处理。这样就避免了重复配置的情况。 b->pos = (u_char*) action; b->last = (u_char*) action+actionLen; b->memory = 1; b->last_buf = 1; contentLen+=actionLen; } else{ //没有action参数就输出 No Action b->pos = noaction; b->last = noaction+sizeof(noaction)-1; b->memory = 1; b->last_buf = 1; contentLen+=sizeof(noaction)-1; } /////////////////////////// r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = contentLen; rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } return ngx_http_output_filter(r, &out); } //根据不同的配置参数设置不同的处理函数。 char* ngx_http_rw_setup(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); ngx_str_t* args; args=cf->args->elts; if(ngx_strcmp("1",args[1].data)==0){ clcf->handler = ngx_http_pw1_handler; } else if(ngx_strcmp("2",args[1].data)==0){ clcf->handler = ngx_http_pw2_handler; } else{ clcf->handler = ngx_http_pw3_handler; } return NGX_CONF_OK; }
您好,想请教一下,我想在内部rewrite到一个页面.这个要怎么实现么?
最简单的方法是输出HTTP 302响应(附加Location头),跳转到你想要的页面。