January 22, 2011 分类: ASM/C/C++, Linux     作者: hoverlees     留言: 2

前两篇文章看了后相信可以写出五花八门的有用的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到一个页面.这个要怎么实现么?
2013-10-18 14:52:07
hoverlees
最简单的方法是输出HTTP 302响应(附加Location头),跳转到你想要的页面。
2013-10-20 11:21:52
我来留个言

您的电子邮箱我一定会保密的哦!

昵称

邮箱

评论内容