499
|
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
|
573
|
13 static ngx_int_t ngx_http_variable_request(ngx_http_request_t *r,
|
|
14 ngx_http_variable_value_t *v, uintptr_t data);
|
637
|
15 static void ngx_http_variable_request_set_size(ngx_http_request_t *r,
|
|
16 ngx_http_variable_value_t *v, uintptr_t data);
|
573
|
17 static ngx_int_t ngx_http_variable_header(ngx_http_request_t *r,
|
|
18 ngx_http_variable_value_t *v, uintptr_t data);
|
|
19 static ngx_int_t ngx_http_variable_headers(ngx_http_request_t *r,
|
|
20 ngx_http_variable_value_t *v, uintptr_t data);
|
577
|
21
|
|
22 static ngx_int_t ngx_http_variable_unknown_header_in(ngx_http_request_t *r,
|
573
|
23 ngx_http_variable_value_t *v, uintptr_t data);
|
577
|
24 static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
|
|
25 ngx_http_variable_value_t *v, uintptr_t data);
|
|
26 static ngx_int_t ngx_http_variable_unknown_header(ngx_http_variable_value_t *v,
|
|
27 ngx_str_t *var, ngx_list_part_t *part, size_t prefix);
|
|
28
|
573
|
29 static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r,
|
|
30 ngx_http_variable_value_t *v, uintptr_t data);
|
|
31 static ngx_int_t ngx_http_variable_remote_addr(ngx_http_request_t *r,
|
|
32 ngx_http_variable_value_t *v, uintptr_t data);
|
|
33 static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r,
|
|
34 ngx_http_variable_value_t *v, uintptr_t data);
|
|
35 static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r,
|
|
36 ngx_http_variable_value_t *v, uintptr_t data);
|
|
37 static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r,
|
|
38 ngx_http_variable_value_t *v, uintptr_t data);
|
|
39 static ngx_int_t ngx_http_variable_document_root(ngx_http_request_t *r,
|
|
40 ngx_http_variable_value_t *v, uintptr_t data);
|
|
41 static ngx_int_t ngx_http_variable_request_filename(ngx_http_request_t *r,
|
|
42 ngx_http_variable_value_t *v, uintptr_t data);
|
|
43 static ngx_int_t ngx_http_variable_request_method(ngx_http_request_t *r,
|
|
44 ngx_http_variable_value_t *v, uintptr_t data);
|
|
45 static ngx_int_t ngx_http_variable_remote_user(ngx_http_request_t *r,
|
|
46 ngx_http_variable_value_t *v, uintptr_t data);
|
611
|
47 static ngx_int_t ngx_http_variable_body_bytes_sent(ngx_http_request_t *r,
|
|
48 ngx_http_variable_value_t *v, uintptr_t data);
|
629
|
49 static ngx_int_t ngx_http_variable_request_completion(ngx_http_request_t *r,
|
|
50 ngx_http_variable_value_t *v, uintptr_t data);
|
499
|
51
|
|
52
|
509
|
53 /*
|
|
54 * TODO:
|
|
55 * Apache CGI: AUTH_TYPE, PATH_INFO (null), PATH_TRANSLATED
|
|
56 * REMOTE_HOST (null), REMOTE_IDENT (null),
|
|
57 * SERVER_SOFTWARE
|
|
58 *
|
571
|
59 * Apache SSI: DOCUMENT_NAME, LAST_MODIFIED, USER_NAME (file owner)
|
509
|
60 */
|
499
|
61
|
509
|
62 static ngx_http_variable_t ngx_http_core_variables[] = {
|
|
63
|
637
|
64 { ngx_string("http_host"), NULL, ngx_http_variable_header,
|
533
|
65 offsetof(ngx_http_request_t, headers_in.host), 0, 0 },
|
499
|
66
|
637
|
67 { ngx_string("http_user_agent"), NULL, ngx_http_variable_header,
|
533
|
68 offsetof(ngx_http_request_t, headers_in.user_agent), 0, 0 },
|
509
|
69
|
637
|
70 { ngx_string("http_referer"), NULL, ngx_http_variable_header,
|
533
|
71 offsetof(ngx_http_request_t, headers_in.referer), 0, 0 },
|
499
|
72
|
|
73 #if (NGX_HTTP_GZIP)
|
637
|
74 { ngx_string("http_via"), NULL, ngx_http_variable_header,
|
533
|
75 offsetof(ngx_http_request_t, headers_in.via), 0, 0 },
|
499
|
76 #endif
|
|
77
|
|
78 #if (NGX_HTTP_PROXY)
|
637
|
79 { ngx_string("http_x_forwarded_for"), NULL, ngx_http_variable_header,
|
533
|
80 offsetof(ngx_http_request_t, headers_in.x_forwarded_for), 0, 0 },
|
499
|
81 #endif
|
|
82
|
637
|
83 { ngx_string("http_cookie"), NULL, ngx_http_variable_headers,
|
533
|
84 offsetof(ngx_http_request_t, headers_in.cookies), 0, 0 },
|
515
|
85
|
637
|
86 { ngx_string("content_length"), NULL, ngx_http_variable_header,
|
533
|
87 offsetof(ngx_http_request_t, headers_in.content_length), 0, 0 },
|
509
|
88
|
637
|
89 { ngx_string("content_type"), NULL, ngx_http_variable_header,
|
533
|
90 offsetof(ngx_http_request_t, headers_in.content_type), 0, 0 },
|
509
|
91
|
637
|
92 { ngx_string("host"), NULL, ngx_http_variable_host, 0, 0, 0 },
|
509
|
93
|
637
|
94 { ngx_string("remote_addr"), NULL, ngx_http_variable_remote_addr, 0, 0, 0 },
|
499
|
95
|
637
|
96 { ngx_string("remote_port"), NULL, ngx_http_variable_remote_port, 0, 0, 0 },
|
509
|
97
|
637
|
98 { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 },
|
509
|
99
|
637
|
100 { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 },
|
509
|
101
|
637
|
102 { ngx_string("server_protocol"), NULL, ngx_http_variable_request,
|
533
|
103 offsetof(ngx_http_request_t, http_protocol), 0, 0 },
|
509
|
104
|
637
|
105 { ngx_string("request_uri"), NULL, ngx_http_variable_request,
|
533
|
106 offsetof(ngx_http_request_t, unparsed_uri), 0, 0 },
|
499
|
107
|
637
|
108 { ngx_string("uri"), NULL, ngx_http_variable_request,
|
|
109 offsetof(ngx_http_request_t, uri),
|
|
110 NGX_HTTP_VAR_NOCACHABLE, 0 },
|
|
111
|
|
112 { ngx_string("document_uri"), NULL, ngx_http_variable_request,
|
573
|
113 offsetof(ngx_http_request_t, uri),
|
|
114 NGX_HTTP_VAR_NOCACHABLE, 0 },
|
|
115
|
637
|
116 { ngx_string("request"), NULL, ngx_http_variable_request,
|
569
|
117 offsetof(ngx_http_request_t, request_line), 0, 0 },
|
|
118
|
637
|
119 { ngx_string("document_root"), NULL,
|
|
120 ngx_http_variable_document_root, 0, 0, 0 },
|
509
|
121
|
637
|
122 { ngx_string("query_string"), NULL, ngx_http_variable_request,
|
509
|
123 offsetof(ngx_http_request_t, args),
|
533
|
124 NGX_HTTP_VAR_NOCACHABLE, 0 },
|
499
|
125
|
637
|
126 { ngx_string("args"), NULL, ngx_http_variable_request,
|
589
|
127 offsetof(ngx_http_request_t, args),
|
|
128 NGX_HTTP_VAR_NOCACHABLE, 0 },
|
|
129
|
637
|
130 { ngx_string("request_filename"), NULL,
|
|
131 ngx_http_variable_request_filename, 0,
|
533
|
132 NGX_HTTP_VAR_NOCACHABLE, 0 },
|
509
|
133
|
637
|
134 { ngx_string("server_name"), NULL, ngx_http_variable_request,
|
533
|
135 offsetof(ngx_http_request_t, server_name), 0, 0 },
|
509
|
136
|
637
|
137 { ngx_string("request_method"), NULL,
|
|
138 ngx_http_variable_request_method, 0, 0, 0 },
|
509
|
139
|
637
|
140 { ngx_string("remote_user"), NULL, ngx_http_variable_remote_user, 0, 0, 0 },
|
509
|
141
|
637
|
142 { ngx_string("body_bytes_sent"), NULL, ngx_http_variable_body_bytes_sent,
|
611
|
143 0, 0, 0 },
|
|
144
|
637
|
145 { ngx_string("request_completion"), NULL,
|
|
146 ngx_http_variable_request_completion,
|
629
|
147 0, 0, 0 },
|
|
148
|
637
|
149 { ngx_string("limit_rate"), ngx_http_variable_request_set_size,
|
|
150 ngx_http_variable_request,
|
|
151 offsetof(ngx_http_request_t, limit_rate),
|
|
152 NGX_HTTP_VAR_CHANGABLE|NGX_HTTP_VAR_NOCACHABLE, 0 },
|
|
153
|
|
154 { ngx_null_string, NULL, NULL, 0, 0, 0 }
|
499
|
155 };
|
|
156
|
|
157
|
577
|
158 ngx_http_variable_value_t ngx_http_variable_null_value =
|
|
159 ngx_http_variable("");
|
|
160 ngx_http_variable_value_t ngx_http_variable_true_value =
|
|
161 ngx_http_variable("1");
|
|
162
|
|
163
|
499
|
164 ngx_http_variable_t *
|
509
|
165 ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
|
|
166 {
|
611
|
167 ngx_int_t rc;
|
509
|
168 ngx_uint_t i;
|
611
|
169 ngx_hash_key_t *key;
|
509
|
170 ngx_http_variable_t *v;
|
|
171 ngx_http_core_main_conf_t *cmcf;
|
|
172
|
|
173 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
|
174
|
611
|
175 key = cmcf->variables_keys->keys.elts;
|
|
176 for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) {
|
|
177 if (name->len != key[i].key.len
|
|
178 || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0)
|
509
|
179 {
|
|
180 continue;
|
|
181 }
|
|
182
|
611
|
183 v = key[i].value;
|
|
184
|
|
185 if (!(v->flags & NGX_HTTP_VAR_CHANGABLE)) {
|
509
|
186 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
187 "the duplicate \"%V\" variable", name);
|
|
188 return NULL;
|
|
189 }
|
|
190
|
611
|
191 return v;
|
509
|
192 }
|
|
193
|
611
|
194 v = ngx_palloc(cf->pool, sizeof(ngx_http_variable_t));
|
509
|
195 if (v == NULL) {
|
|
196 return NULL;
|
|
197 }
|
|
198
|
|
199 v->name.len = name->len;
|
|
200 v->name.data = ngx_palloc(cf->pool, name->len);
|
|
201 if (v->name.data == NULL) {
|
|
202 return NULL;
|
|
203 }
|
|
204
|
|
205 for (i = 0; i < name->len; i++) {
|
|
206 v->name.data[i] = ngx_tolower(name->data[i]);
|
|
207 }
|
|
208
|
637
|
209 v->set_handler = NULL;
|
|
210 v->get_handler = NULL;
|
509
|
211 v->data = 0;
|
|
212 v->flags = flags;
|
533
|
213 v->index = 0;
|
509
|
214
|
611
|
215 rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0);
|
|
216
|
|
217 if (rc == NGX_ERROR) {
|
|
218 return NULL;
|
|
219 }
|
|
220
|
|
221 if (rc == NGX_BUSY) {
|
|
222 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
223 "conflicting variable name \"%V\"", name);
|
|
224 return NULL;
|
|
225 }
|
|
226
|
509
|
227 return v;
|
|
228 }
|
|
229
|
|
230
|
|
231 ngx_int_t
|
|
232 ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name)
|
499
|
233 {
|
501
|
234 ngx_uint_t i;
|
|
235 ngx_http_variable_t *v;
|
499
|
236 ngx_http_core_main_conf_t *cmcf;
|
|
237
|
|
238 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
|
239
|
501
|
240 v = cmcf->variables.elts;
|
|
241
|
|
242 if (v == NULL) {
|
499
|
243 if (ngx_array_init(&cmcf->variables, cf->pool, 4,
|
|
244 sizeof(ngx_http_variable_t)) == NGX_ERROR)
|
|
245 {
|
509
|
246 return NGX_ERROR;
|
499
|
247 }
|
501
|
248
|
|
249 } else {
|
|
250 for (i = 0; i < cmcf->variables.nelts; i++) {
|
|
251 if (name->len != v[i].name.len
|
|
252 || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
|
|
253 {
|
|
254 continue;
|
|
255 }
|
|
256
|
509
|
257 return i;
|
501
|
258 }
|
499
|
259 }
|
|
260
|
501
|
261 v = ngx_array_push(&cmcf->variables);
|
|
262 if (v == NULL) {
|
509
|
263 return NGX_ERROR;
|
499
|
264 }
|
|
265
|
501
|
266 v->name.len = name->len;
|
|
267 v->name.data = ngx_palloc(cf->pool, name->len);
|
|
268 if (v->name.data == NULL) {
|
509
|
269 return NGX_ERROR;
|
501
|
270 }
|
499
|
271
|
501
|
272 for (i = 0; i < name->len; i++) {
|
509
|
273 v->name.data[i] = ngx_tolower(name->data[i]);
|
501
|
274 }
|
|
275
|
637
|
276 v->set_handler = NULL;
|
|
277 v->get_handler = NULL;
|
501
|
278 v->data = 0;
|
509
|
279 v->flags = 0;
|
533
|
280 v->index = cmcf->variables.nelts - 1;
|
501
|
281
|
509
|
282 return cmcf->variables.nelts - 1;
|
499
|
283 }
|
|
284
|
|
285
|
|
286 ngx_http_variable_value_t *
|
|
287 ngx_http_get_indexed_variable(ngx_http_request_t *r, ngx_uint_t index)
|
|
288 {
|
501
|
289 ngx_http_variable_t *v;
|
499
|
290 ngx_http_core_main_conf_t *cmcf;
|
|
291
|
|
292 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
|
|
293
|
553
|
294 if (cmcf->variables.nelts <= index) {
|
499
|
295 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
|
296 "unknown variable index: %d", index);
|
|
297 return NULL;
|
|
298 }
|
|
299
|
573
|
300 if (r->variables[index].not_found || r->variables[index].valid) {
|
|
301 return &r->variables[index];
|
501
|
302 }
|
499
|
303
|
501
|
304 v = cmcf->variables.elts;
|
499
|
305
|
637
|
306 if (v[index].get_handler(r, &r->variables[index], v[index].data)
|
|
307 == NGX_OK)
|
|
308 {
|
573
|
309 if (v[index].flags & NGX_HTTP_VAR_NOCACHABLE) {
|
|
310 r->variables[index].no_cachable = 1;
|
499
|
311 }
|
573
|
312
|
|
313 return &r->variables[index];
|
499
|
314 }
|
|
315
|
573
|
316 return NULL;
|
|
317 }
|
|
318
|
|
319
|
|
320 ngx_http_variable_value_t *
|
|
321 ngx_http_get_flushed_variable(ngx_http_request_t *r, ngx_uint_t index)
|
|
322 {
|
|
323 ngx_http_variable_value_t *v;
|
|
324
|
|
325 v = &r->variables[index];
|
|
326
|
|
327 if (v->valid) {
|
|
328 if (!v->no_cachable) {
|
|
329 return v;
|
|
330 }
|
|
331
|
|
332 v->valid = 0;
|
|
333 v->not_found = 0;
|
509
|
334 }
|
501
|
335
|
573
|
336 return ngx_http_get_indexed_variable(r, index);
|
499
|
337 }
|
|
338
|
|
339
|
|
340 ngx_http_variable_value_t *
|
635
|
341 ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key,
|
|
342 ngx_uint_t nowarn)
|
499
|
343 {
|
501
|
344 ngx_http_variable_t *v;
|
573
|
345 ngx_http_variable_value_t *vv;
|
499
|
346 ngx_http_core_main_conf_t *cmcf;
|
|
347
|
|
348 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
|
|
349
|
611
|
350 v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);
|
499
|
351
|
611
|
352 if (v) {
|
|
353 if (v->flags & NGX_HTTP_VAR_INDEXED) {
|
|
354 return ngx_http_get_indexed_variable(r, v->index);
|
519
|
355
|
|
356 } else {
|
499
|
357
|
573
|
358 vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
|
501
|
359
|
637
|
360 if (vv && v->get_handler(r, vv, v->data) == NGX_OK) {
|
573
|
361 return vv;
|
|
362 }
|
509
|
363
|
573
|
364 return NULL;
|
|
365 }
|
509
|
366 }
|
|
367
|
|
368 vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
|
|
369 if (vv == NULL) {
|
|
370 return NULL;
|
|
371 }
|
|
372
|
573
|
373 if (ngx_strncmp(name->data, "http_", 5) == 0) {
|
499
|
374
|
577
|
375 if (ngx_http_variable_unknown_header_in(r, vv, (uintptr_t) name)
|
|
376 == NGX_OK)
|
|
377 {
|
|
378 return vv;
|
|
379 }
|
|
380
|
|
381 return NULL;
|
|
382 }
|
|
383
|
|
384 if (ngx_strncmp(name->data, "sent_http_", 10) == 0) {
|
|
385
|
|
386 if (ngx_http_variable_unknown_header_out(r, vv, (uintptr_t) name)
|
|
387 == NGX_OK)
|
573
|
388 {
|
|
389 return vv;
|
|
390 }
|
499
|
391
|
|
392 return NULL;
|
|
393 }
|
|
394
|
635
|
395 vv->not_found = 1;
|
573
|
396
|
635
|
397 if (nowarn == 0) {
|
|
398 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
399 "unknown \"%V\" variable", name);
|
|
400 }
|
499
|
401
|
501
|
402 return vv;
|
499
|
403 }
|
|
404
|
|
405
|
573
|
406 static ngx_int_t
|
|
407 ngx_http_variable_request(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
|
408 uintptr_t data)
|
|
409 {
|
|
410 ngx_str_t *s;
|
|
411
|
|
412 s = (ngx_str_t *) ((char *) r + data);
|
|
413
|
|
414 if (s->data) {
|
|
415 v->len = s->len;
|
|
416 v->valid = 1;
|
|
417 v->no_cachable = 0;
|
|
418 v->not_found = 0;
|
|
419 v->data = s->data;
|
|
420
|
|
421 } else {
|
|
422 v->not_found = 1;
|
|
423 }
|
|
424
|
|
425 return NGX_OK;
|
|
426 }
|
|
427
|
|
428
|
637
|
429 static void
|
|
430 ngx_http_variable_request_set_size(ngx_http_request_t *r,
|
|
431 ngx_http_variable_value_t *v, uintptr_t data)
|
|
432 {
|
|
433 ssize_t s, *sp;
|
|
434 ngx_str_t val;
|
|
435
|
|
436 val.len = v->len & 0xffff;
|
|
437 val.data = v->data;
|
|
438
|
|
439 s = ngx_parse_size(&val);
|
|
440
|
|
441 if (s == NGX_ERROR) {
|
|
442 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
443 "invalid size \"%V\"", &val);
|
|
444 return;
|
|
445 }
|
|
446
|
|
447 sp = (ssize_t *) ((char *) r + data);
|
|
448
|
|
449 *sp = s;
|
|
450
|
|
451 return;
|
|
452 }
|
|
453
|
|
454
|
573
|
455 static ngx_int_t
|
|
456 ngx_http_variable_header(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
|
457 uintptr_t data)
|
515
|
458 {
|
573
|
459 ngx_table_elt_t *h;
|
|
460
|
|
461 h = *(ngx_table_elt_t **) ((char *) r + data);
|
|
462
|
|
463 if (h) {
|
|
464 v->len = h->value.len;
|
|
465 v->valid = 1;
|
|
466 v->no_cachable = 0;
|
|
467 v->not_found = 0;
|
|
468 v->data = h->value.data;
|
|
469
|
|
470 } else {
|
|
471 v->not_found = 1;
|
|
472 }
|
|
473
|
|
474 return NGX_OK;
|
|
475 }
|
|
476
|
|
477
|
|
478 static ngx_int_t
|
|
479 ngx_http_variable_headers(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
|
480 uintptr_t data)
|
|
481 {
|
|
482 size_t len;
|
|
483 u_char *p;
|
|
484 ngx_uint_t i;
|
|
485 ngx_array_t *a;
|
|
486 ngx_table_elt_t **h;
|
515
|
487
|
|
488 a = (ngx_array_t *) ((char *) r + data);
|
|
489
|
|
490 if (a->nelts == 0) {
|
573
|
491 v->not_found = 1;
|
|
492 return NGX_OK;
|
515
|
493 }
|
|
494
|
573
|
495 v->valid = 1;
|
|
496 v->no_cachable = 0;
|
|
497 v->not_found = 0;
|
515
|
498
|
|
499 h = a->elts;
|
|
500
|
|
501 if (a->nelts == 1) {
|
573
|
502 v->len = (*h)->value.len;
|
|
503 v->data = (*h)->value.data;
|
515
|
504
|
573
|
505 return NGX_OK;
|
515
|
506 }
|
|
507
|
573
|
508 len = (size_t) - (ssize_t) (sizeof("; ") - 1);
|
|
509
|
|
510 for (i = 0; i < a->nelts; i++) {
|
|
511 len += h[i]->value.len + sizeof("; ") - 1;
|
515
|
512 }
|
|
513
|
573
|
514 p = ngx_palloc(r->pool, len);
|
|
515 if (p == NULL) {
|
|
516 return NGX_ERROR;
|
|
517 }
|
|
518
|
|
519 v->len = len;
|
|
520 v->data = p;
|
515
|
521
|
|
522 for (i = 0; /* void */ ; i++) {
|
573
|
523 p = ngx_copy(p, h[i]->value.data, h[i]->value.len);
|
515
|
524
|
|
525 if (i == a->nelts - 1) {
|
|
526 break;
|
|
527 }
|
|
528
|
|
529 *p++ = ';'; *p++ = ' ';
|
|
530 }
|
|
531
|
573
|
532 return NGX_OK;
|
515
|
533 }
|
|
534
|
|
535
|
573
|
536 static ngx_int_t
|
577
|
537 ngx_http_variable_unknown_header_in(ngx_http_request_t *r,
|
|
538 ngx_http_variable_value_t *v, uintptr_t data)
|
|
539 {
|
|
540 return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
|
|
541 &r->headers_in.headers.part,
|
|
542 sizeof("http_") - 1);
|
|
543 }
|
|
544
|
|
545
|
|
546 static ngx_int_t
|
|
547 ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
|
573
|
548 ngx_http_variable_value_t *v, uintptr_t data)
|
499
|
549 {
|
577
|
550 return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
|
|
551 &r->headers_out.headers.part,
|
|
552 sizeof("sent_http_") - 1);
|
|
553 }
|
501
|
554
|
577
|
555
|
|
556 static ngx_int_t
|
|
557 ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var,
|
|
558 ngx_list_part_t *part, size_t prefix)
|
|
559 {
|
573
|
560 u_char ch;
|
|
561 ngx_uint_t i, n;
|
|
562 ngx_table_elt_t *header;
|
499
|
563
|
|
564 header = part->elts;
|
|
565
|
|
566 for (i = 0; /* void */ ; i++) {
|
|
567
|
|
568 if (i >= part->nelts) {
|
|
569 if (part->next == NULL) {
|
|
570 break;
|
|
571 }
|
|
572
|
|
573 part = part->next;
|
|
574 header = part->elts;
|
|
575 i = 0;
|
|
576 }
|
|
577
|
577
|
578 for (n = 0; n + prefix < var->len && n < header[i].key.len; n++) {
|
499
|
579 ch = header[i].key.data[n];
|
|
580
|
509
|
581 if (ch >= 'A' && ch <= 'Z') {
|
|
582 ch |= 0x20;
|
499
|
583
|
|
584 } else if (ch == '-') {
|
|
585 ch = '_';
|
|
586 }
|
|
587
|
577
|
588 if (var->data[n + prefix] != ch) {
|
499
|
589 break;
|
|
590 }
|
|
591 }
|
|
592
|
577
|
593 if (n + prefix == var->len) {
|
573
|
594 v->len = header[i].value.len;
|
|
595 v->valid = 1;
|
|
596 v->no_cachable = 0;
|
|
597 v->not_found = 0;
|
|
598 v->data = header[i].value.data;
|
499
|
599
|
573
|
600 return NGX_OK;
|
499
|
601 }
|
|
602 }
|
|
603
|
573
|
604 v->not_found = 1;
|
|
605
|
|
606 return NGX_OK;
|
509
|
607 }
|
|
608
|
|
609
|
573
|
610 static ngx_int_t
|
|
611 ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v,
|
|
612 uintptr_t data)
|
509
|
613 {
|
573
|
614 if (r->headers_in.host) {
|
|
615 v->len = r->headers_in.host_name_len;
|
|
616 v->data = r->headers_in.host->value.data;
|
509
|
617
|
573
|
618 } else {
|
|
619 v->len = r->server_name.len;
|
|
620 v->data = r->server_name.data;
|
509
|
621 }
|
|
622
|
573
|
623 v->valid = 1;
|
|
624 v->no_cachable = 0;
|
|
625 v->not_found = 0;
|
509
|
626
|
573
|
627 return NGX_OK;
|
499
|
628 }
|
|
629
|
|
630
|
573
|
631 static ngx_int_t
|
|
632 ngx_http_variable_remote_addr(ngx_http_request_t *r,
|
|
633 ngx_http_variable_value_t *v, uintptr_t data)
|
499
|
634 {
|
573
|
635 v->len = r->connection->addr_text.len;
|
|
636 v->valid = 1;
|
|
637 v->no_cachable = 0;
|
|
638 v->not_found = 0;
|
|
639 v->data = r->connection->addr_text.data;
|
499
|
640
|
573
|
641 return NGX_OK;
|
499
|
642 }
|
|
643
|
|
644
|
573
|
645 static ngx_int_t
|
|
646 ngx_http_variable_remote_port(ngx_http_request_t *r,
|
|
647 ngx_http_variable_value_t *v, uintptr_t data)
|
499
|
648 {
|
573
|
649 ngx_uint_t port;
|
|
650 struct sockaddr_in *sin;
|
509
|
651
|
573
|
652 v->len = 0;
|
|
653 v->valid = 1;
|
|
654 v->no_cachable = 0;
|
|
655 v->not_found = 0;
|
509
|
656
|
573
|
657 v->data = ngx_palloc(r->pool, sizeof("65535") - 1);
|
|
658 if (v->data == NULL) {
|
|
659 return NGX_ERROR;
|
509
|
660 }
|
|
661
|
|
662 /* AF_INET only */
|
577
|
663
|
509
|
664 if (r->connection->sockaddr->sa_family == AF_INET) {
|
|
665 sin = (struct sockaddr_in *) r->connection->sockaddr;
|
577
|
666
|
509
|
667 port = ntohs(sin->sin_port);
|
577
|
668
|
509
|
669 if (port > 0 && port < 65536) {
|
573
|
670 v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
|
509
|
671 }
|
|
672 }
|
|
673
|
573
|
674 return NGX_OK;
|
509
|
675 }
|
|
676
|
|
677
|
573
|
678 static ngx_int_t
|
|
679 ngx_http_variable_server_addr(ngx_http_request_t *r,
|
|
680 ngx_http_variable_value_t *v, uintptr_t data)
|
509
|
681 {
|
573
|
682 socklen_t len;
|
|
683 ngx_connection_t *c;
|
|
684 struct sockaddr_in sin;
|
499
|
685
|
573
|
686 v->data = ngx_palloc(r->pool, INET_ADDRSTRLEN);
|
|
687 if (v->data == NULL) {
|
|
688 return NGX_ERROR;
|
509
|
689 }
|
|
690
|
|
691 c = r->connection;
|
|
692
|
|
693 if (r->in_addr == 0) {
|
|
694 len = sizeof(struct sockaddr_in);
|
|
695 if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) {
|
|
696 ngx_log_error(NGX_LOG_CRIT, c->log,
|
|
697 ngx_socket_errno, "getsockname() failed");
|
573
|
698 return NGX_ERROR;
|
509
|
699 }
|
|
700
|
|
701 r->in_addr = sin.sin_addr.s_addr;
|
|
702 }
|
|
703
|
573
|
704 v->len = ngx_inet_ntop(c->listening->family, &r->in_addr,
|
|
705 v->data, INET_ADDRSTRLEN);
|
|
706 v->valid = 1;
|
|
707 v->no_cachable = 0;
|
|
708 v->not_found = 0;
|
499
|
709
|
573
|
710 return NGX_OK;
|
499
|
711 }
|
|
712
|
|
713
|
573
|
714 static ngx_int_t
|
|
715 ngx_http_variable_server_port(ngx_http_request_t *r,
|
|
716 ngx_http_variable_value_t *v, uintptr_t data)
|
509
|
717 {
|
573
|
718 v->len = r->port_text->len - 1;
|
|
719 v->valid = 1;
|
|
720 v->no_cachable = 0;
|
|
721 v->not_found = 0;
|
|
722 v->data = r->port_text->data + 1;
|
509
|
723
|
573
|
724 return NGX_OK;
|
509
|
725 }
|
|
726
|
|
727
|
573
|
728 static ngx_int_t
|
|
729 ngx_http_variable_document_root(ngx_http_request_t *r,
|
|
730 ngx_http_variable_value_t *v, uintptr_t data)
|
499
|
731 {
|
573
|
732 ngx_http_core_loc_conf_t *clcf;
|
509
|
733
|
|
734 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
|
735
|
573
|
736 v->len = clcf->root.len;
|
|
737 v->valid = 1;
|
|
738 v->no_cachable = 0;
|
|
739 v->not_found = 0;
|
|
740 v->data = clcf->root.data;
|
509
|
741
|
573
|
742 return NGX_OK;
|
509
|
743 }
|
|
744
|
|
745
|
573
|
746 static ngx_int_t
|
|
747 ngx_http_variable_request_filename(ngx_http_request_t *r,
|
|
748 ngx_http_variable_value_t *v, uintptr_t data)
|
509
|
749 {
|
573
|
750 ngx_str_t path;
|
499
|
751
|
573
|
752 if (ngx_http_map_uri_to_path(r, &path, 0) == NULL) {
|
|
753 return NGX_ERROR;
|
557
|
754 }
|
509
|
755
|
557
|
756 /* ngx_http_map_uri_to_path() allocates memory for terminating '\0' */
|
509
|
757
|
573
|
758 v->len = path.len - 1;
|
|
759 v->valid = 1;
|
|
760 v->no_cachable = 0;
|
|
761 v->not_found = 0;
|
|
762 v->data = path.data;
|
499
|
763
|
573
|
764 return NGX_OK;
|
499
|
765 }
|
|
766
|
|
767
|
573
|
768 static ngx_int_t
|
|
769 ngx_http_variable_request_method(ngx_http_request_t *r,
|
|
770 ngx_http_variable_value_t *v, uintptr_t data)
|
561
|
771 {
|
573
|
772 if (r->method_name.data) {
|
|
773 if (r->upstream && r->upstream->method.len) {
|
|
774 v->len = r->upstream->method.len;
|
|
775 v->data = r->upstream->method.data;
|
561
|
776
|
573
|
777 } else {
|
|
778 v->len = r->method_name.len;
|
|
779 v->data = r->method_name.data;
|
|
780 }
|
|
781
|
|
782 v->valid = 1;
|
|
783 v->no_cachable = 0;
|
|
784 v->not_found = 0;
|
|
785
|
|
786 } else {
|
|
787 v->not_found = 1;
|
561
|
788 }
|
|
789
|
573
|
790 return NGX_OK;
|
561
|
791 }
|
|
792
|
|
793
|
573
|
794 static ngx_int_t
|
|
795 ngx_http_variable_remote_user(ngx_http_request_t *r,
|
|
796 ngx_http_variable_value_t *v, uintptr_t data)
|
539
|
797 {
|
573
|
798 ngx_int_t rc;
|
539
|
799
|
|
800 rc = ngx_http_auth_basic_user(r);
|
|
801
|
|
802 if (rc == NGX_DECLINED) {
|
573
|
803 v->not_found = 1;
|
|
804 return NGX_OK;
|
539
|
805 }
|
|
806
|
|
807 if (rc == NGX_ERROR) {
|
573
|
808 return NGX_ERROR;
|
539
|
809 }
|
|
810
|
573
|
811 v->len = r->headers_in.user.len;
|
|
812 v->valid = 1;
|
|
813 v->no_cachable = 0;
|
|
814 v->not_found = 0;
|
|
815 v->data = r->headers_in.user.data;
|
571
|
816
|
573
|
817 return NGX_OK;
|
571
|
818 }
|
|
819
|
|
820
|
611
|
821 static ngx_int_t
|
|
822 ngx_http_variable_body_bytes_sent(ngx_http_request_t *r,
|
|
823 ngx_http_variable_value_t *v, uintptr_t data)
|
|
824 {
|
|
825 off_t sent;
|
|
826 u_char *p;
|
|
827
|
|
828 sent = r->connection->sent - r->header_size;
|
|
829
|
|
830 if (sent < 0) {
|
|
831 sent = 0;
|
|
832 }
|
|
833
|
|
834 p = ngx_palloc(r->pool, NGX_OFF_T_LEN);
|
|
835 if (p == NULL) {
|
|
836 return NGX_ERROR;
|
|
837 }
|
|
838
|
|
839 v->len = ngx_sprintf(p, "%O", sent) - p;
|
|
840 v->valid = 1;
|
|
841 v->no_cachable = 0;
|
|
842 v->not_found = 0;
|
|
843 v->data = p;
|
|
844
|
|
845 return NGX_OK;
|
|
846 }
|
|
847
|
|
848
|
629
|
849 static ngx_int_t
|
|
850 ngx_http_variable_request_completion(ngx_http_request_t *r,
|
|
851 ngx_http_variable_value_t *v, uintptr_t data)
|
|
852 {
|
|
853 if (r->request_complete) {
|
|
854 v->len = 2;
|
|
855 v->valid = 1;
|
|
856 v->no_cachable = 0;
|
|
857 v->not_found = 0;
|
|
858 v->data = (u_char *) "OK";
|
|
859
|
|
860 return NGX_OK;
|
|
861 }
|
|
862
|
|
863 v->len = 0;
|
|
864 v->valid = 1;
|
|
865 v->no_cachable = 0;
|
|
866 v->not_found = 0;
|
|
867 v->data = (u_char *) "";
|
|
868
|
|
869 return NGX_OK;
|
|
870 }
|
|
871
|
|
872
|
499
|
873 ngx_int_t
|
509
|
874 ngx_http_variables_add_core_vars(ngx_conf_t *cf)
|
499
|
875 {
|
611
|
876 ngx_int_t rc;
|
|
877 ngx_http_variable_t *v;
|
501
|
878 ngx_http_core_main_conf_t *cmcf;
|
499
|
879
|
509
|
880 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
|
881
|
611
|
882 cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,
|
|
883 sizeof(ngx_hash_keys_arrays_t));
|
|
884 if (cmcf->variables_keys == NULL) {
|
|
885 return NGX_ERROR;
|
|
886 }
|
|
887
|
|
888 cmcf->variables_keys->pool = cf->pool;
|
|
889 cmcf->variables_keys->temp_pool = cf->pool;
|
|
890
|
|
891 if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)
|
|
892 != NGX_OK)
|
509
|
893 {
|
499
|
894 return NGX_ERROR;
|
|
895 }
|
|
896
|
611
|
897 for (v = ngx_http_core_variables; v->name.len; v++) {
|
|
898 rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v,
|
|
899 NGX_HASH_READONLY_KEY);
|
|
900
|
|
901 if (rc == NGX_OK) {
|
|
902 continue;
|
499
|
903 }
|
|
904
|
611
|
905 if (rc == NGX_BUSY) {
|
|
906 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
907 "conflicting variable name \"%V\"", &v->name);
|
|
908 }
|
|
909
|
|
910 return NGX_ERROR;
|
499
|
911 }
|
|
912
|
|
913 return NGX_OK;
|
|
914 }
|
509
|
915
|
|
916
|
|
917 ngx_int_t
|
|
918 ngx_http_variables_init_vars(ngx_conf_t *cf)
|
|
919 {
|
|
920 ngx_uint_t i, n;
|
611
|
921 ngx_hash_key_t *key;
|
|
922 ngx_hash_init_t hash;
|
509
|
923 ngx_http_variable_t *v, *av;
|
|
924 ngx_http_core_main_conf_t *cmcf;
|
|
925
|
|
926 /* set the handlers for the indexed http variables */
|
|
927
|
|
928 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
|
929
|
|
930 v = cmcf->variables.elts;
|
611
|
931 key = cmcf->variables_keys->keys.elts;
|
509
|
932
|
|
933 for (i = 0; i < cmcf->variables.nelts; i++) {
|
|
934
|
611
|
935 for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
|
509
|
936
|
611
|
937 if (v[i].name.len == key[n].key.len
|
|
938 && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len)
|
509
|
939 == 0)
|
|
940 {
|
611
|
941 av = key[n].value;
|
|
942
|
637
|
943 v[i].get_handler = av->get_handler;
|
611
|
944 v[i].data = av->data;
|
527
|
945
|
611
|
946 av->flags |= NGX_HTTP_VAR_INDEXED;
|
|
947 v[i].flags = av->flags;
|
509
|
948
|
611
|
949 av->index = i;
|
533
|
950
|
509
|
951 goto next;
|
|
952 }
|
|
953 }
|
|
954
|
|
955 if (ngx_strncmp(v[i].name.data, "http_", 5) == 0) {
|
637
|
956 v[i].get_handler = ngx_http_variable_unknown_header_in;
|
577
|
957 v[i].data = (uintptr_t) &v[i].name;
|
|
958
|
|
959 continue;
|
|
960 }
|
|
961
|
|
962 if (ngx_strncmp(v[i].name.data, "sent_http_", 10) == 0) {
|
637
|
963 v[i].get_handler = ngx_http_variable_unknown_header_out;
|
509
|
964 v[i].data = (uintptr_t) &v[i].name;
|
|
965
|
|
966 continue;
|
|
967 }
|
|
968
|
|
969 ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
|
970 "unknown \"%V\" variable", &v[i].name);
|
|
971
|
|
972 return NGX_ERROR;
|
|
973
|
|
974 next:
|
|
975 continue;
|
|
976 }
|
|
977
|
|
978
|
611
|
979 for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
|
|
980 av = key[n].value;
|
|
981
|
|
982 if (av->flags & NGX_HTTP_VAR_NOHASH) {
|
|
983 key[n].key.data = NULL;
|
583
|
984 }
|
|
985 }
|
|
986
|
|
987
|
611
|
988 hash.hash = &cmcf->variables_hash;
|
|
989 hash.key = ngx_hash_key;
|
|
990 hash.max_size = cmcf->variables_hash_max_size;
|
|
991 hash.bucket_size = cmcf->variables_hash_bucket_size;
|
|
992 hash.name = "variables_hash";
|
|
993 hash.pool = cf->pool;
|
|
994 hash.temp_pool = NULL;
|
509
|
995
|
611
|
996 if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts,
|
|
997 cmcf->variables_keys->keys.nelts)
|
581
|
998 != NGX_OK)
|
509
|
999 {
|
|
1000 return NGX_ERROR;
|
|
1001 }
|
|
1002
|
611
|
1003 cmcf->variables_keys = NULL;
|
509
|
1004
|
|
1005 return NGX_OK;
|
|
1006 }
|