nginx 实现反向代理offsetof怎么实现的

浅谈Nginx请求过滤模块的开发
由于Nginx(engine x)具有高性能、可反向代理等优秀特点,因而在市场中被广泛的使用。客户端发起的HTTP请求一般是通过Nginx代理到后台服务器去处理的,因此我们可以通过Nginx监控客户端的HTTP请求内容是否包含攻击内容,从而判断请求的合法性。为了实现对HTTP请求的过滤,我们需要开发一个可加载到Nginx中的自定义filter模块。其功能为:当检测到客户端的请求包含攻击时,阻止Nginx转发HTTP请求给后台服务器,直接返回禁止访问的HTTP请求响应给客户端。实现这样的一个功能模块,我们需要先了解Nginx的handler模块及其加载实现方式的相关知识。
Handler模块
Handler模块简介
&&&&&&&&Handler模块就是接受来自客户端的请求并产生输出的模块。Handler模块处理的结果通常有三种情况:处理成功、处理失败或者拒绝处理。
模块的基本结构
& & & &模块配置结构
& & & &Nginx的配置信息分成了几个作用域(scope,有时也称作上下文),这就是main、server以及location。同样的每个模块提供的配置指令也可以出现在这几个作用域里。那对于这三个作用域的配置信息,每个模块就需要定义三个不同的数据结构去进行存储。当然,不是每个模块都会在这三个作用域都提供配置指令的。那么也就不一定每个模块都需要定义三个数据结构去存储这些配置信息了。视模块的实现而言,需要几个就定义几个。
对于模块配置信息的定义,命名习惯是ngx_http_&module name&_(main|srv|loc)_conf_t。例如本文中的filter module定义:
typedef struct {
&&&& ngx_str_t fm_string;
&&&& ngx_flag_t fm_enable;
} ngx_http_fm_loc_conf_t;
&&&&&&&&模块配置指令
& & & &&一个模块的配置指令定义在一个静态数组中。例如本文中的ngx_http_fm_commands定义:
static ngx_command_t ngx_fm_commands[] = {
&&&&&&&&&& { ngx_string(&ngx_fm_string&), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_SIF_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_FLAG, ngx_fm_string, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_fm_loc_conf_t, fm_string), NULL },
&&&&&&&&&& { ngx_string(&ngx_fm_enable&), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_SIF_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_FLAG, ngx_fm_enable, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_fm_loc_conf_t, fm_enable), NULL },
ngx_null_command
&&&&&&&&&& 注意:数组最后需要以ngx_null_command作为结尾元素。
& & & &模块上下文结构
&&&&&&&ngx_http_module_t静态变量提供了一组回调函数指针,这些函数有在创建存储配置信息的对象函数,也有在创建前、后调用的函数。Nginx 会在合适的时间对它们进行调用。这里不对其结构作详细介绍了,想要详细了解可参考阿里的《Nginx开发从入门到精通》文档。例如本文中ngx_http_fm_module_ctx的定义:
static ngx_http_module_t ngx_http_fm_module_ctx = {
&&&&&&&&&& NULL, ngx_fm_module_init, NULL, NULL, NULL, NULL, ngx_fm_create_loc_conf, NULL
& & & &模块的定义
&&&&&&&开发nginx模块,需要定义一个ngx_module_t类型的变量来说明这个模块本身的信息。它包含配置信息、上下文信息等。加载模块的上层代码需要通过定义的模块结构获取这些信息。本文实例中的模块定义为:
ngx_module_t ngx_http_fm_module = {
&&&&&&&&&& NGX_MODULE_V1, &ngx_http_fm_module_ctx, ngx_http_fm_commands, NGX_HTTP_MODULE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NGX_MODULE_V1_PADDING
Handler模块的基本结构
& & & & Handler模块需要提供一个处理函数,用来处理客户端的请求,可以在函数中生成内容,也可以拒绝,让其他的handler去处理。这个函数的原型为:
typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
r结构体是http请求。该函数处理成功返回NGX_OK,失败返回NGX_ERROR,拒绝处理(留给后续handler进行处理)返回NGX_DECLINE。
Handler模块的挂载
Nginx模块的挂载方式有两种。一种就是按处理阶段挂载;另外一种就是按需挂载。
按处理阶段挂载
为了更精细的控制对于客户端请求的处理过程,nginx把这个处理过程划分成了11个阶段。分别为:
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(日志模块处理阶段)
注意:有几个阶段时特例,它不调用挂载地任何的handler,所以你就不用挂载在这些阶段了。
& & & &NGX_HTTP_FIND_CONFIG_PHASE(配置查找阶段)
NGX_HTTP_POST_ACCESS_PHASE访问权限检查提交阶段)
NGX_HTTP_POST_REWRITE_PHASE(请求地址重写提交阶段)
NGX_HTTP_TRY_FILES_PHASE(配置项try_files处理阶段)
本文中的实例采用的是按阶段挂载handler,实现如下:
static ngx_int_t ngx_fm_module_init(ngx_conf_t *cf) {
&&&& ngx_http_handler_pt&&&&&&&&&&&& *h;
&&&& ngx_http_core_main_conf_t *
&&&& cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
&&&& h = ngx_array_push(&cmcf-&phases[NGX_HTTP_POST_READ_PHASE].handlers);
&&&& if(h == NULL) {
&&&&&&&&&& return NGX_ERROR;
&&&& *h = ngx_http_fm_
&&&& return NGX_OK;
& & &以这种方式挂载的handler也被称为content handler。当一个请求进来以后,nginx从NGX_HTTP_POST_READ_PHASE阶段开始依次执行每个阶段中所有handler。执行到 NGX_HTTP_CONTENT_PHASE阶段的时候,如果这个location有一个对应的content handler模块,那么就去执行这个content handler模块真正的处理函数。否则继续依次执行NGX_HTTP_CONTENT_PHASE阶段中所有content phase handlers,直到某个函数处理返回NGX_OK或者NGX_ERROR。换句话说,当某个location处理到NGX_HTTP_CONTENT_PHASE阶段时,如果有content handler模块,那么NGX_HTTP_CONTENT_PHASE挂载的所有content phase handlers都不会被执行了。但是使用这个方法挂载上去的handler有一个特点是必须在NGX_HTTP_CONTENT_PHASE阶段才能执行到。如果你想自己的handler在更早的阶段执行,那就不要使用这种挂载方式。那么在什么情况会使用这种方式来挂载呢?一般情况下,某个模块对某个location进行了处理以后,发现符合自己处理的逻辑,而且也没有必要再调用NGX_HTTP_CONTENT_PHASE阶段的其它handler进行处理的时候,就动态挂载上这个handler。
Handler模块的实现
&&&&&& &通过上面的知识点,我们对Handler模块结构有了一些了解,那么接下来我们就需要将这些知识点串联起来,实现一个模块。实现步骤:
1)&& 编写模块基本结构,包含模块定义,模块上下文结构,模块的配置结构等。
2)&& 编写handler挂载函数,我们选择按处理阶段挂载。
3)&& 编写handler处理函数,我们将要实现的filter功能就是通过此函数实现。
4)&& 编译模块到Nginx之中,我们通过编译Nginx源码添加模块的方式将自定义的filter模块整合到Nginx。
如果你的开发系统环境是Linux,那么一切省事了,编译和安装可以在一个环境中进行。由于我的开发环境是OS X,所以需要在虚拟机中建立一个Linux系统作为编译、安装环境。
Linux编译环境配置
Linux译环境配置编
yum install gcc gcc-c++ openssl-devel pcre-devel wget
下载、解压Nginx源码
wget http://nginx.org/download/nginx-1.13.4.tar.gz
tar xzvf nginx-1.13.4.tar.gz
开发环境配置
OS X环境下默认没有gcc,需要通过应用商店安装Xcode,安装完后测试执行gcc –v。
下载适用于系统版本的Eclipse CDT,解压运行,选择工作目录。
开发filter module实例
1)创建C项目,命名为“ngx_fm”
2)添加Nginx模块的源码文件“ngx_filter.h、 ngx_filter.c ”,定义模块配置结构体和声明函数。
&&&&&&&&&& 3)具体实现源码请见:/leonSecTec/ngx_fm。HTTP请求过滤功能的实现主要是在ngx_http_fm_handler函数中添加了一个关键字搜索函数,用以检查客户端提交的请求中是否包含攻击。
int ngx_search(unsigned char* str) {
&&&& const char *keyword[] = { &alert&, &or&, &and&, &../& };
&&&& for(i = 0; i & 4; i++) {
&&&&&&&&&& if(ngx_sunday((const char*)str, keyword[i]) != NULL) {
&&&&&&&&&&&&&&& return 1;
&&&&&&&&&& }
&&&& return 0;
static ngx_int_t ngx_http_fm_handler(ngx_http_request_t *r) {
&&&& if(ngx_search(r-&uri.data)) {
&&&&&&&&&& ngx_log_error(NGX_LOG_WARN, r-&connection-&log, 0, &attack data: %s&, r-&uri.data);
&&&&&&&&&& return NGX_HTTP_NOT_ALLOWED;
&&&& return NGX_OK;
4)&& 在项目中添加“config”编译配置文件,其内容如下
ngx_addon_name=ngx_http_fm_module
HTTP_MODULES=&$HTTP_MODULES ngx_http_fm_module&
NGX_ADDON_SRCS=&$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_search.c $ngx_addon_dir/src/ngx_filter.c&
编译添加了自定义filter 模块的Nginx源码
将filter模块源码上传到Linux虚拟机中的Nginx源码解压后的同级目录,然后执行如下命令,进行代码编译及安装。
cd nginx-1.13.4
./configure --prefix=/opt/nginx --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-mail --with-mail_ssl_module --add-module=../ngx_fm
make install
1)启动运行Nginx,即执行 /opt/nginx/sbin/nginx &
2)访问当前测试站点,查看日志输出,即执行cat /opt/nginx/log/error.log。当提交具有攻击关键字的请求时,filter模块会因检测到请求中具有攻击关键字而返回405。
本文中的实例实现的仅仅是针对HTTP GET请求URL内容的关键字过滤。从严格的角度来说,其并未实现真正意义上的请求过滤处理。要实现一个完整的过滤功能模块,需要对HTTP请求行、消息报头以及请求正文进行检测。由于时间有限,这里暂时不作深入研究,有兴趣的同学可以深入了解下。
参考文档:
http://tengine.taobao.org/book/
快来写下你的想法吧!
饿了么安全应急响应中心。
(C) 安全脉搏博客访问: 100821
博文数量: 27
博客积分: 725
博客等级: 上士
技术积分: 347
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
我们知道在Nginx中许多地方需要用到定时器,典型的就是当accept接受连接后,如果在一定时间内还未收到用户请求,就超时断开连接。那这个是如何实现的呢?这个问题我是相当的有兴趣。先说点题外话,有一天晚上,坐在床上看电影,但是又到了睡觉的时间,我怕管不住自己,于是就想让电脑定时关机,印象中好像cron还是at可以执行定时任务,但是自己又不会用,打开man page看那些英文觉得太麻烦,干脆自己做一个定时器算了,于是花了半个多小时终于搞定了,但是问题是这个定时器只能处理单任务,倘若有很多任务都要定时,并及时处理超时任务该怎么办呢?在Nginx中找到了答案,于是在之前那个迷你版的Nginx上增加了这个超时的机制。不过我的实现跟Nginx的又有所区别,Nginx是用红黑树来管理定时器的,我为了简单,用了一个排序的双链表管理定时器,其实原理是一样的。
先给出这个双链的接口
#ifndef _LIST_H_INCLUDED__
#define _LIST_H_INCLUDED__
struct list_node {
&&&&struct&&&&list_node&&&&&&&&*next, *prev;
static inline void
list_init(struct list_node *node)
&&&&node-&next = node-&prev = node;
static inline void
__list_add(struct list_node *prev, struct list_node *next, struct list_node *node)
&&&&prev-&next = node;
&&&&node-&prev = prev;
&&&&next-&prev = node;
&&&&node-&next = next;
static inline void
list_add_prev(struct list_node *cur, struct list_node *node)
&&&&__list_add(cur-&prev, cur, node);
static inline void
list_add_next(struct list_node *cur, struct list_node *node)
&&&&__list_add(cur, cur-&next, node);
static inline void
list_add_tail(struct list_node *head, struct list_node *node)
&&&&list_add_prev(head, node);
static inline void
list_add_head(struct list_node *head, struct list_node *node)
&&&&list_add_next(head, node);
static inline void
__list_del(struct list_node *prev, struct list_node *next)
&&&&prev-&next = next;
&&&&next-&prev = prev;
static inline void
list_del(struct list_node *node)
&&&&__list_del(node-&prev, node-&next);
&&&&node-&prev = node-&next = NULL;
static inline int
list_empty(struct list_node *head)
&&&&return head-&next == head-&prev && head-&next == head;
#define offsetof(ptype, element) \
&&&&((int) &(((ptype) 0)-&element))
#define container_of(list, ptype, element) \
&&&&((ptype) ((char *) (list) - offsetof(ptype, element)))
#define list_entry(list, ptype, element) \
&&&&container_of(list, ptype, element)
#define list_first_entry(head, ptype, element) \
&&&&list_entry((head)-&next, ptype, element)
#define list_for_each_entry(node, head, element) \
&&&&for (node = list_entry((head)-&next, typeof(node), element); \
&&&&&&&&&&&&&(node-&element) != (head); \
&&&&&&&&&&&&node = list_entry(node-&element.next, typeof(node), element))
接下来就是定时器的接口了
#ifndef _TIMER_H_INCLUDED__
#define _TIMER_H_INCLUDED__
#include "httpd.h"
struct timer {
&&&&int&&&&&&&&&&&&&&&&&&&&key;
&&&&struct list_node&&&&list;
static inline void
list_timer_del(struct timer *p)
&&&&list_del(&p-&list);
static inline void
list_timer_add(struct timer *timer, struct timer *p)
&&&&struct timer *t;
&&&&list_init(&p-&list);
&&&&list_for_each_entry(t, &timer-&list, list) {
&&&&&&&&if (t-&key &= p-&key) break;
&&&&list_add_prev(&t-&list, &p-&list);
static inline struct timer *
list_timer_entry(struct list_node *list)
&&&&return list_entry(list, struct timer *, list);
static inline struct timer *
list_timer_first_entry(struct timer *timer)
&&&&return list_first_entry(&timer-&list, struct timer *, list);
static inline int
list_timer_empty(struct timer *timer)
&&&&return list_empty(&timer-&list);
static inline struct timer *
list_min_timer(struct timer *timer)
&&&&if (list_timer_empty(timer))
&&&&&&&&return NULL;
&&&&return list_timer_first_entry(timer);
#include "httpd.h"
#define TIMER_LAZY_DELAY&&&&300
struct timer httpd_timer;
httpd_event_timer_init(void)
&&&&list_init(&httpd_timer.list);
httpd_timer_del(event_t *ev)
&&&&list_timer_del(&ev-&timer);
&&&&ev-&timer_set = 0;
httpd_timer_add(event_t *ev, int msec)
&&&&int &&&&diff;
&&&&uint&&&&key;
&&&&key = httpd_current_msec + msec;
&&&&if (ev-&timer_set) {
&&&&&&&&diff = key - ev-&timer.key;
&&&&&&&&if (abs(diff) & TIMER_LAZY_DELAY)
&&&&&&&&&&&&return;
&&&&&&&&httpd_timer_del(ev);
&&&&ev-&timer.key = key;
&&&&list_timer_add(&httpd_timer, &ev-&timer);
&&&&ev-&timer_set = 1;
httpd_find_timer(void)
&&&&int&&&&&&&&&&&& timer;
&&&&struct timer *p;
&&&&if (list_timer_empty(&httpd_timer))
&&&&&&&&return (uint_t)-1;
&&&&p = list_timer_entry(httpd_timer.list.next);
&&&&timer = p-&key - httpd_current_msec;
&&&&return timer & 0 ? timer : 0;
httpd_event_expire_timers(void)
&&&&struct timer &&&&*p;
&&&&event_t&&&&&&&&&&&&*ev;
&&&&for ( ; p = list_timer_first_entry(&httpd_timer); ) {
&&&&&&&&if ((int)p-&key - (int)httpd_current_msec &= 0) {
&&&&&&&&&&&&ev = list_entry(p, event_t *, timer);
&&&&&&&&&&&&
&&&&&&&&&&&&list_timer_del(p);
&&&&&&&&&&&&
&&&&&&&&&&&&ev-&timer_set = 0;
&&&&&&&&&&&&ev-&timedout = 1;
&&&&&&&&&&&&
&&&&&&&&&&&&ev-&handler(ev);
&&&&&&&&break;
#include "httpd.h"
uint_t&&&&httpd_current_msec;
httpd_time_update(void)
&&&&struct timeval tv;
&&&&gettimeofday(&tv, NULL);
&&&&httpd_current_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
我们再来看看整个处理流程是怎么样的呢?
#include "httpd.h"
int &&&&&&&&&&&&&&&&ep = -1;
int&&&&&&&&&&&&&&&&&&&&nevents;
struct epoll_event&&&&*event_list;
epoll_init(void)
&&&&if ((ep = epoll_create(1024)) & 0)
&&&&&&&&err_sys("epoll create error");
&&&&event_list = calloc(1024, sizeof(struct epoll_event));
&&&&nevents = 1024;
epoll_add_event(event_t *ev, int event, int flag)
&&&&struct epoll_event &&&& ee;
&&&&struct connection&&&&*c;
&&&&c = ev-&connection;
&&&&ee.events = event | flag;
&&&&ee.data.ptr = c;
&&&&if (epoll_ctl(ep, EPOLL_CTL_ADD, c-&fd, &ee) & 0)
&&&&&&&&err_sys("epoll ctl error");
&&&&ev-&active = 1;
epoll_process_events(void)
&&&&int &&&&&&&&&&&& i, events, revents, timer;
&&&&connection_t&&&&*c;
&&&&event_t&&&&&&&&&&&&*rev, *wev;
&&&&timer = httpd_find_timer();
&&&&events = epoll_wait(ep, event_list, nevents, timer);
&&&&httpd_time_update();
&&&&for (i = 0; i & events; i++) {
&&&&&&&&c = event_list[i].data.ptr;
&&&&&&&&revents = event_list[i].events;
&&&&&&&&rev = c-&rev;
&&&&&&&&if ((revents & EPOLLIN) && rev-&active) {
&&&&&&&&&&&&rev-&handler(rev);
&&&&&&&&wev = c-&wev;
&&&&&&&&if ((revents & EPOLLOUT) && wev-&active) {
&&&&&&&&&&&&wev-&handler(wev);
&&&&httpd_event_expire_timers();
阅读(1873) | 评论(0) | 转发(1) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。nginx 全局变量及防DDOS攻击的简单配置_网络管理_动态网站制作指南
nginx 全局变量及防DDOS攻击的简单配置
来源:人气:382
  经常需要配置Nginx ,其中有许多以 $ 开头的变量,经常需要查阅nginx 所支持的变量。  可能是对 Ngixn资源不熟悉,干脆就直接读,分析出支持的变量。  Nginx支持的http变量实现在 ngx_http_variables.c 的 ngx_http_core_variables存储实现:  ngx_http_core_variables  1 static ngx_http_variable_t ngx_http_core_variables[] = {  2  3 { ngx_string(&http_host&), NULL, ngx_http_variable_header,  4 offsetof(ngx_http_request_t, headers_in.host), 0, 0 },  5  6 { ngx_string(&http_user_agent&), NULL, ngx_http_variable_header,  7 offsetof(ngx_http_request_t, headers_in.user_agent), 0, 0 },  8  9 { ngx_string(&http_referer&), NULL, ngx_http_variable_header,  10 offsetof(ngx_http_request_t, headers_in.referer), 0, 0 },  11  12 #if (NGX_HTTP_GZ)  13 { ngx_string(&http_via&), NULL, ngx_http_variable_header,  14 offsetof(ngx_http_request_t, headers_in.via), 0, 0 },  15 #endif  16  17 #if (NGX_HTTP_OXY || NGX_HTTP_REALIP)  18 { ngx_string(&http_x_forwarded_for&), NULL,
ngx_http_variable_header,  19 offsetof(ngx_http_request_t, headers_in.x_forwarded_for), 0, 0 },  20 #endif  21  22 { ngx_string(&http_cookie&), NULL, ngx_http_variable_headers,  23 offsetof(ngx_http_request_t, headers_in.cookies), 0, 0 },  24  25 { ngx_string(&content_length&), NULL, ngx_http_variable_header,  26 offsetof(ngx_http_request_t, headers_in.content_length), 0, 0 },  27  28 { ngx_string(&content_type&), NULL, ngx_http_variable_header,  29 offsetof(ngx_http_request_t, headers_in.content_type), 0, 0 },  30  31 { ngx_string(&host&), NULL, ngx_http_variable_host, 0, 0, 0 },  32  33 { ngx_string(&binary_remote_addr&), NULL,  34 ngx_http_variable_binary_remote_addr, 0, 0, 0 },  35  36 { ngx_string(&remote_addr&), NULL, ngx_http_variable_remote_addr, 0, 0,
0 },  37  38 { ngx_string(&remote_port&), NULL, ngx_http_variable_remote_port, 0, 0,
0 },  39  40 { ngx_string(&server_addr&), NULL, ngx_http_variable_server_addr, 0, 0,
0 },  41  42 { ngx_string(&server_port&), NULL, ngx_http_variable_server_port, 0, 0,
0 },  43  44 { ngx_string(&server_protocol&), NULL, ngx_http_variable_request,  45 offsetof(ngx_http_request_t, http_protocol), 0, 0 },  46  47 { ngx_string(&scheme&), NULL, ngx_http_variable_scheme, 0, 0, 0 },  48  49 { ngx_string(&request_uri&), NULL, ngx_http_variable_request,  50 offsetof(ngx_http_request_t, unparsed_uri), 0, 0 },  51  52 { ngx_string(&uri&), NULL, ngx_http_variable_request,  53 offsetof(ngx_http_request_t, uri),  54 NGX_HTTP_VAR_NOCACHEABLE, 0 },  55  56 { ngx_string(&document_uri&), NULL, ngx_http_variable_request,  57 offsetof(ngx_http_request_t, uri),  58 NGX_HTTP_VAR_NOCACHEABLE, 0 },  59  60 { ngx_string(&request&), NULL, ngx_http_variable_request_line, 0, 0, 0
},  61  62 { ngx_string(&document_root&), NULL,  63 ngx_http_variable_document_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },  64  65 { ngx_string(&realpath_root&), NULL,  66 ngx_http_variable_realpath_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },  67  68 { ngx_string(&query_string&), NULL, ngx_http_variable_request,  69 offsetof(ngx_http_request_t, args),  70 NGX_HTTP_VAR_NOCACHEABLE, 0 },  71  72 { ngx_string(&args&),  73 ngx_http_variable_request_set,  74 ngx_http_variable_request,  75 offsetof(ngx_http_request_t, args),  76 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },  77  78 { ngx_string(&is_args&), NULL, ngx_http_variable_is_args,  79 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },  80  81 { ngx_string(&request_filename&), NULL,  82 ngx_http_variable_request_filename, 0,  83 NGX_HTTP_VAR_NOCACHEABLE, 0 },  84  85 { ngx_string(&server_name&), NULL, ngx_http_variable_server_name, 0, 0,
0 },  86  87 { ngx_string(&request_method&), NULL,  88 ngx_http_variable_request_method, 0,  89 NGX_HTTP_VAR_NOCACHEABLE, 0 },  90  91 { ngx_string(&remote_user&), NULL, ngx_http_variable_remote_user, 0, 0,
0 },  92  93 { ngx_string(&body_bytes_sent&), NULL,
ngx_http_variable_body_bytes_sent,  94 0, 0, 0 },  95  96 { ngx_string(&request_completion&), NULL,  97 ngx_http_variable_request_completion,  98 0, 0, 0 },  99  100 { ngx_string(&request_body&), NULL,  101 ngx_http_variable_request_body,  102 0, 0, 0 },  103  104 { ngx_string(&request_body_file&), NULL,  105 ngx_http_variable_request_body_file,  106 0, 0, 0 },  107  108 { ngx_string(&sent_http_content_type&), NULL,  109 ngx_http_variable_sent_content_type, 0, 0, 0 },  110  111 { ngx_string(&sent_http_content_length&), NULL,  112 ngx_http_variable_sent_content_length, 0, 0, 0 },  113  114 { ngx_string(&sent_http_location&), NULL,  115 ngx_http_variable_sent_location, 0, 0, 0 },  116  117 { ngx_string(&sent_http_last_modified&), NULL,  118 ngx_http_variable_sent_last_modified, 0, 0, 0 },  119  120 { ngx_string(&sent_http_connection&), NULL,  121 ngx_http_variable_sent_connection, 0, 0, 0 },  122  123 { ngx_string(&sent_http_keep_alive&), NULL,  124 ngx_http_variable_sent_keep_alive, 0, 0, 0 },  125  126 { ngx_string(&sent_http_transfer_encoding&), NULL,  127 ngx_http_variable_sent_transfer_encoding, 0, 0, 0 },  128  129 { ngx_string(&sent_http_cache_control&), NULL,
ngx_http_variable_headers,  130 offsetof(ngx_http_request_t, headers_out.cache_control), 0, 0 },  131  132 { ngx_string(&limit_rate&), ngx_http_variable_request_set_size,  133 ngx_http_variable_request_get_size,  134 offsetof(ngx_http_request_t, limit_rate),  135 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },  136  137 { ngx_string(&nginx_version&), NULL,
ngx_http_variable_nginx_version,  138 0, 0, 0 },  139  140 { ngx_string(&hostname&), NULL, ngx_http_variable_hostname,  141 0, 0, 0 },  142  143 { ngx_string(&pid&), NULL, ngx_http_variable_pid,  144 0, 0, 0 },  145  146 { ngx_null_string, NULL, NULL, 0, 0, 0 }  147 };
优质网站模板

我要回帖

更多关于 nginx实现跨域 的文章

 

随机推荐