48
|
1
|
|
2 /*
|
|
3 * Copyright (C) Igor Sysoev
|
|
4 */
|
|
5
|
|
6
|
|
7 #include <ngx_config.h>
|
|
8 #include <ngx_core.h>
|
|
9 #include <ngx_event.h>
|
|
10 #include <ngx_http.h>
|
|
11
|
|
12
|
|
13 #define NGX_HTTP_VARS_HASH_PRIME 29
|
|
14
|
|
15 #define ngx_http_vars_hash_key(key, vn) \
|
|
16 { \
|
|
17 ngx_uint_t n; \
|
|
18 for (key = 0, n = 0; n < (vn)->len; n++) { \
|
|
19 key += (vn)->data[n]; \
|
|
20 } \
|
|
21 key %= NGX_HTTP_VARS_HASH_PRIME; \
|
|
22 }
|
|
23
|
|
24
|
|
25 static ngx_http_variable_value_t *
|
|
26 ngx_http_variable_header(ngx_http_request_t *r, uintptr_t data);
|
|
27 static ngx_http_variable_value_t *
|
50
|
28 ngx_http_variable_unknown_header(ngx_http_request_t *r, uintptr_t data);
|
48
|
29 static ngx_http_variable_value_t *
|
|
30 ngx_http_variable_remote_addr(ngx_http_request_t *r, uintptr_t data);
|
|
31 static ngx_http_variable_value_t *
|
|
32 ngx_http_variable_uri(ngx_http_request_t *r, uintptr_t data);
|
|
33 static ngx_http_variable_value_t *
|
|
34 ngx_http_variable_query_string(ngx_http_request_t *r, uintptr_t data);
|
|
35
|
|
36
|
|
37 static ngx_array_t *ngx_http_core_variables_hash;
|
|
38
|
|
39
|
|
40 static ngx_http_core_variable_t ngx_http_core_variables[] = {
|
|
41
|
|
42 { ngx_string("HTTP_HOST"), ngx_http_variable_header,
|
|
43 offsetof(ngx_http_headers_in_t, host) },
|
|
44
|
|
45 { ngx_string("HTTP_USER_AGENT"), ngx_http_variable_header,
|
|
46 offsetof(ngx_http_headers_in_t, user_agent) },
|
|
47
|
|
48 { ngx_string("HTTP_REFERER"), ngx_http_variable_header,
|
|
49 offsetof(ngx_http_headers_in_t, referer) },
|
|
50
|
|
51 #if (NGX_HTTP_GZIP)
|
|
52 { ngx_string("HTTP_VIA"), ngx_http_variable_header,
|
|
53 offsetof(ngx_http_headers_in_t, via) },
|
|
54 #endif
|
|
55
|
|
56 #if (NGX_HTTP_PROXY)
|
|
57 { ngx_string("HTTP_X_FORWARDED_FOR"), ngx_http_variable_header,
|
|
58 offsetof(ngx_http_headers_in_t, x_forwarded_for) },
|
|
59 #endif
|
|
60
|
|
61 { ngx_string("REMOTE_ADDR"), ngx_http_variable_remote_addr, 0 },
|
|
62
|
|
63 { ngx_string("DOCUMENT_URI"), ngx_http_variable_uri, 0 },
|
|
64
|
|
65 { ngx_string("QUERY_STRING"), ngx_http_variable_query_string, 0 },
|
|
66
|
|
67 { ngx_null_string, NULL, 0 }
|
|
68 };
|
|
69
|
|
70
|
|
71 ngx_http_variable_t *
|
50
|
72 ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t set)
|
48
|
73 {
|
50
|
74 ngx_uint_t i;
|
|
75 ngx_http_variable_t *v;
|
48
|
76 ngx_http_core_main_conf_t *cmcf;
|
|
77
|
|
78 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
|
79
|
50
|
80 v = cmcf->variables.elts;
|
|
81
|
|
82 if (v == NULL) {
|
48
|
83 if (ngx_array_init(&cmcf->variables, cf->pool, 4,
|
|
84 sizeof(ngx_http_variable_t)) == NGX_ERROR)
|
|
85 {
|
|
86 return NULL;
|
|
87 }
|
50
|
88
|
|
89 } else {
|
|
90 for (i = 0; i < cmcf->variables.nelts; i++) {
|
|
91 if (name->len != v[i].name.len
|
|
92 || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
|
|
93 {
|
|
94 continue;
|
|
95 }
|
|
96
|
|
97 if (set && v[i].handler) {
|
|
98 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
99 "the duplicate \"%V\" variable", name);
|
|
100 return NULL;
|
|
101 }
|
|
102
|
|
103 return &v[i];
|
|
104 }
|
48
|
105 }
|
|
106
|
50
|
107 v = ngx_array_push(&cmcf->variables);
|
|
108 if (v == NULL) {
|
48
|
109 return NULL;
|
|
110 }
|
|
111
|
50
|
112 v->name.len = name->len;
|
|
113 v->name.data = ngx_palloc(cf->pool, name->len);
|
|
114 if (v->name.data == NULL) {
|
|
115 return NULL;
|
|
116 }
|
48
|
117
|
50
|
118 for (i = 0; i < name->len; i++) {
|
|
119 v->name.data[i] = ngx_toupper(name->data[i]);
|
|
120 }
|
|
121
|
|
122 v->index = cmcf->variables.nelts - 1;
|
|
123 v->handler = NULL;
|
|
124 v->data = 0;
|
|
125
|
|
126 return v;
|
48
|
127 }
|
|
128
|
|
129
|
|
130 ngx_http_variable_value_t *
|
|
131 ngx_http_get_indexed_variable(ngx_http_request_t *r, ngx_uint_t index)
|
|
132 {
|
50
|
133 ngx_http_variable_t *v;
|
|
134 ngx_http_variable_value_t *vv;
|
48
|
135 ngx_http_core_main_conf_t *cmcf;
|
|
136
|
|
137 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
|
|
138
|
|
139 if (cmcf->variables.elts == NULL || cmcf->variables.nelts <= index) {
|
|
140 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
|
141 "unknown variable index: %d", index);
|
|
142 return NULL;
|
|
143 }
|
|
144
|
50
|
145 if (r->variables && r->variables[index]) {
|
|
146 return r->variables[index];
|
|
147 }
|
48
|
148
|
50
|
149 v = cmcf->variables.elts;
|
48
|
150
|
50
|
151 vv = v[index].handler(r, v[index].data);
|
48
|
152
|
50
|
153 if (r->variables == NULL) {
|
|
154 r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts
|
|
155 * sizeof(ngx_http_variable_value_t *));
|
|
156 if (r->variables == NULL) {
|
|
157 return NULL;
|
48
|
158 }
|
|
159 }
|
|
160
|
50
|
161 r->variables[index] = vv;
|
|
162
|
|
163 return vv;
|
48
|
164 }
|
|
165
|
|
166
|
|
167 ngx_http_variable_value_t *
|
|
168 ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name)
|
|
169 {
|
|
170 ngx_uint_t i, key;
|
50
|
171 ngx_http_variable_t *v;
|
|
172 ngx_http_core_variable_t *cv;
|
48
|
173 ngx_http_core_main_conf_t *cmcf;
|
|
174
|
|
175 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
|
|
176
|
50
|
177 v = cmcf->variables.elts;
|
|
178 for (i = 0; i < cmcf->variables.nelts; i++) {
|
|
179 if (v[i].name.len != name->len) {
|
|
180 continue;
|
|
181 }
|
48
|
182
|
50
|
183 if (ngx_strncmp(v[i].name.data, name->data, name->len) == 0) {
|
|
184 return ngx_http_get_indexed_variable(r, v[i].index);
|
|
185 }
|
48
|
186 }
|
|
187
|
|
188 ngx_http_vars_hash_key(key, name);
|
|
189
|
50
|
190 cv = ngx_http_core_variables_hash[key].elts;
|
48
|
191 for (i = 0; i < ngx_http_core_variables_hash[key].nelts; i++) {
|
50
|
192 if (cv[i].name.len != name->len) {
|
48
|
193 continue;
|
|
194 }
|
|
195
|
50
|
196 if (ngx_strncmp(cv[i].name.data, name->data, name->len) == 0) {
|
|
197 return cv[i].handler(r, cv[i].data);
|
|
198 }
|
48
|
199 }
|
|
200
|
50
|
201 if (ngx_strncmp(name->data, "HTTP_", 5) == 0) {
|
|
202 return ngx_http_variable_unknown_header(r, (uintptr_t) name);
|
48
|
203 }
|
|
204
|
50
|
205 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
206 "unknown \"%V\" variable", name);
|
|
207
|
|
208 return NGX_HTTP_VARIABLE_NOT_FOUND;
|
48
|
209 }
|
|
210
|
|
211
|
|
212 static ngx_http_variable_value_t *
|
|
213 ngx_http_variable_header(ngx_http_request_t *r, uintptr_t data)
|
|
214 {
|
|
215 ngx_table_elt_t *h;
|
50
|
216 ngx_http_variable_value_t *vv;
|
48
|
217
|
|
218 h = *(ngx_table_elt_t **) ((char *) &r->headers_in + data);
|
|
219
|
|
220 if (h == NULL) {
|
|
221 return NGX_HTTP_VARIABLE_NOT_FOUND;
|
|
222 }
|
|
223
|
50
|
224 vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
|
|
225 if (vv == NULL) {
|
48
|
226 return NULL;
|
|
227 }
|
|
228
|
50
|
229 vv->value = 0;
|
|
230 vv->text = h->value;
|
48
|
231
|
50
|
232 return vv;
|
48
|
233 }
|
|
234
|
|
235
|
|
236 static ngx_http_variable_value_t *
|
50
|
237 ngx_http_variable_unknown_header(ngx_http_request_t *r, uintptr_t data)
|
48
|
238 {
|
50
|
239 ngx_str_t *var = (ngx_str_t *) data;
|
|
240
|
48
|
241 u_char ch;
|
|
242 ngx_uint_t i, n;
|
|
243 ngx_list_part_t *part;
|
|
244 ngx_table_elt_t *header;
|
50
|
245 ngx_http_variable_value_t *vv;
|
48
|
246
|
|
247 part = &r->headers_in.headers.part;
|
|
248 header = part->elts;
|
|
249
|
|
250 for (i = 0; /* void */ ; i++) {
|
|
251
|
|
252 if (i >= part->nelts) {
|
|
253 if (part->next == NULL) {
|
|
254 break;
|
|
255 }
|
|
256
|
|
257 part = part->next;
|
|
258 header = part->elts;
|
|
259 i = 0;
|
|
260 }
|
|
261
|
|
262 for (n = 0; n + 5 < var->len && n < header[i].key.len; n++)
|
|
263 {
|
|
264 ch = header[i].key.data[n];
|
|
265
|
|
266 if (ch >= 'a' && ch <= 'z') {
|
|
267 ch &= ~0x20;
|
|
268
|
|
269 } else if (ch == '-') {
|
|
270 ch = '_';
|
|
271 }
|
|
272
|
|
273 if (var->data[n + 5] != ch) {
|
|
274 break;
|
|
275 }
|
|
276 }
|
|
277
|
|
278 if (n + 5 == var->len) {
|
50
|
279 vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
|
|
280 if (vv == NULL) {
|
48
|
281 return NULL;
|
|
282 }
|
|
283
|
50
|
284 vv->value = 0;
|
|
285 vv->text = header[i].value;
|
|
286 return vv;
|
48
|
287 }
|
|
288 }
|
|
289
|
|
290 return NGX_HTTP_VARIABLE_NOT_FOUND;
|
|
291 }
|
|
292
|
|
293
|
|
294 static ngx_http_variable_value_t *
|
|
295 ngx_http_variable_remote_addr(ngx_http_request_t *r, uintptr_t data)
|
|
296 {
|
50
|
297 ngx_http_variable_value_t *vv;
|
48
|
298
|
50
|
299 vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
|
|
300 if (vv == NULL) {
|
48
|
301 return NULL;
|
|
302 }
|
|
303
|
50
|
304 vv->value = 0;
|
|
305 vv->text = r->connection->addr_text;
|
48
|
306
|
50
|
307 return vv;
|
48
|
308 }
|
|
309
|
|
310
|
|
311 static ngx_http_variable_value_t *
|
|
312 ngx_http_variable_uri(ngx_http_request_t *r, uintptr_t data)
|
|
313 {
|
50
|
314 ngx_http_variable_value_t *vv;
|
48
|
315
|
50
|
316 vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
|
|
317 if (vv == NULL) {
|
48
|
318 return NULL;
|
|
319 }
|
|
320
|
50
|
321 vv->value = 0;
|
|
322 vv->text = r->uri;
|
48
|
323
|
50
|
324 return vv;
|
48
|
325 }
|
|
326
|
|
327
|
|
328 static ngx_http_variable_value_t *
|
|
329 ngx_http_variable_query_string(ngx_http_request_t *r, uintptr_t data)
|
|
330 {
|
50
|
331 ngx_http_variable_value_t *vv;
|
48
|
332
|
50
|
333 vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
|
|
334 if (vv == NULL) {
|
48
|
335 return NULL;
|
|
336 }
|
|
337
|
50
|
338 vv->value = 0;
|
|
339 vv->text = r->args;
|
48
|
340
|
50
|
341 return vv;
|
48
|
342 }
|
|
343
|
|
344
|
|
345 ngx_int_t
|
50
|
346 ngx_http_variables_init(ngx_cycle_t *cycle)
|
48
|
347 {
|
50
|
348 ngx_uint_t i, j, key;
|
|
349 ngx_http_variable_t *v;
|
|
350 ngx_http_core_variable_t *cv, *vp;
|
|
351 ngx_http_core_main_conf_t *cmcf;
|
48
|
352
|
|
353 ngx_http_core_variables_hash = ngx_palloc(cycle->pool,
|
|
354 NGX_HTTP_VARS_HASH_PRIME
|
|
355 * sizeof(ngx_array_t));
|
|
356 if (ngx_http_core_variables_hash == NULL) {
|
|
357 return NGX_ERROR;
|
|
358 }
|
|
359
|
|
360 for (i = 0; i < NGX_HTTP_VARS_HASH_PRIME; i++) {
|
|
361 if (ngx_array_init(&ngx_http_core_variables_hash[i], cycle->pool, 4,
|
|
362 sizeof(ngx_http_core_variable_t)) == NGX_ERROR)
|
|
363 {
|
|
364 return NGX_ERROR;
|
|
365 }
|
|
366 }
|
|
367
|
50
|
368 for (cv = ngx_http_core_variables; cv->name.len; cv++) {
|
|
369 ngx_http_vars_hash_key(key, &cv->name);
|
48
|
370
|
50
|
371 vp = ngx_array_push(&ngx_http_core_variables_hash[key]);
|
|
372 if (vp == NULL) {
|
48
|
373 return NGX_ERROR;
|
|
374 }
|
|
375
|
50
|
376 *vp = *cv;
|
|
377 }
|
|
378
|
|
379
|
|
380 cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module);
|
|
381
|
|
382 v = cmcf->variables.elts;
|
|
383 for (i = 0; i < cmcf->variables.nelts; i++) {
|
|
384
|
|
385 if (v[i].handler) {
|
|
386 continue;
|
|
387 }
|
|
388
|
|
389 ngx_http_vars_hash_key(key, &v[i].name);
|
|
390
|
|
391 cv = ngx_http_core_variables_hash[key].elts;
|
|
392 for (j = 0; j < ngx_http_core_variables_hash[key].nelts; j++) {
|
|
393 if (cv[j].name.len != v[i].name.len) {
|
|
394 continue;
|
|
395 }
|
|
396
|
|
397 if (ngx_strncmp(cv[j].name.data, v[i].name.data, v[i].name.len)
|
|
398 == 0)
|
|
399 {
|
|
400 v[i].handler = cv[j].handler;
|
|
401 v[i].data = cv[j].data;
|
|
402 continue;
|
|
403 }
|
|
404 }
|
|
405
|
|
406 if (ngx_strncmp(v[i].name.data, "HTTP_", 5) == 0) {
|
|
407 v[i].handler = ngx_http_variable_unknown_header;
|
|
408 v[i].data = (uintptr_t) &v[i].name;
|
|
409 continue;
|
|
410 }
|
|
411
|
|
412 ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
|
|
413 "unknown \"%V\" variable", &v[i].name);
|
|
414
|
|
415 return NGX_ERROR;
|
48
|
416 }
|
|
417
|
|
418 return NGX_OK;
|
|
419 }
|