comparison src/stream/ngx_stream_variables.c @ 6607:c70b7f4537e1

Stream: variables and script. This is a port of corresponding http code with unrelated features excluded.
author Vladimir Homutov <vl@nginx.com>
date Mon, 04 Jul 2016 16:37:36 +0300
parents
children eb4293155e87
comparison
equal deleted inserted replaced
6606:2f41d383c9c7 6607:c70b7f4537e1
1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_stream.h>
11 #include <nginx.h>
12
13
14 static ngx_int_t ngx_stream_variable_nginx_version(ngx_stream_session_t *s,
15 ngx_stream_variable_value_t *v, uintptr_t data);
16
17
18 static ngx_stream_variable_t ngx_stream_core_variables[] = {
19
20 { ngx_string("nginx_version"), NULL, ngx_stream_variable_nginx_version,
21 0, 0, 0 },
22
23 { ngx_null_string, NULL, NULL, 0, 0, 0 }
24 };
25
26
27 ngx_stream_variable_value_t ngx_stream_variable_null_value =
28 ngx_stream_variable("");
29 ngx_stream_variable_value_t ngx_stream_variable_true_value =
30 ngx_stream_variable("1");
31
32
33 ngx_stream_variable_t *
34 ngx_stream_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
35 {
36 ngx_int_t rc;
37 ngx_uint_t i;
38 ngx_hash_key_t *key;
39 ngx_stream_variable_t *v;
40 ngx_stream_core_main_conf_t *cmcf;
41
42 if (name->len == 0) {
43 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
44 "invalid variable name \"$\"");
45 return NULL;
46 }
47
48 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
49
50 key = cmcf->variables_keys->keys.elts;
51 for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) {
52 if (name->len != key[i].key.len
53 || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0)
54 {
55 continue;
56 }
57
58 v = key[i].value;
59
60 if (!(v->flags & NGX_STREAM_VAR_CHANGEABLE)) {
61 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
62 "the duplicate \"%V\" variable", name);
63 return NULL;
64 }
65
66 return v;
67 }
68
69 v = ngx_palloc(cf->pool, sizeof(ngx_stream_variable_t));
70 if (v == NULL) {
71 return NULL;
72 }
73
74 v->name.len = name->len;
75 v->name.data = ngx_pnalloc(cf->pool, name->len);
76 if (v->name.data == NULL) {
77 return NULL;
78 }
79
80 ngx_strlow(v->name.data, name->data, name->len);
81
82 v->set_handler = NULL;
83 v->get_handler = NULL;
84 v->data = 0;
85 v->flags = flags;
86 v->index = 0;
87
88 rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0);
89
90 if (rc == NGX_ERROR) {
91 return NULL;
92 }
93
94 if (rc == NGX_BUSY) {
95 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
96 "conflicting variable name \"%V\"", name);
97 return NULL;
98 }
99
100 return v;
101 }
102
103
104 ngx_int_t
105 ngx_stream_get_variable_index(ngx_conf_t *cf, ngx_str_t *name)
106 {
107 ngx_uint_t i;
108 ngx_stream_variable_t *v;
109 ngx_stream_core_main_conf_t *cmcf;
110
111 if (name->len == 0) {
112 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
113 "invalid variable name \"$\"");
114 return NGX_ERROR;
115 }
116
117 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
118
119 v = cmcf->variables.elts;
120
121 if (v == NULL) {
122 if (ngx_array_init(&cmcf->variables, cf->pool, 4,
123 sizeof(ngx_stream_variable_t))
124 != NGX_OK)
125 {
126 return NGX_ERROR;
127 }
128
129 } else {
130 for (i = 0; i < cmcf->variables.nelts; i++) {
131 if (name->len != v[i].name.len
132 || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
133 {
134 continue;
135 }
136
137 return i;
138 }
139 }
140
141 v = ngx_array_push(&cmcf->variables);
142 if (v == NULL) {
143 return NGX_ERROR;
144 }
145
146 v->name.len = name->len;
147 v->name.data = ngx_pnalloc(cf->pool, name->len);
148 if (v->name.data == NULL) {
149 return NGX_ERROR;
150 }
151
152 ngx_strlow(v->name.data, name->data, name->len);
153
154 v->set_handler = NULL;
155 v->get_handler = NULL;
156 v->data = 0;
157 v->flags = 0;
158 v->index = cmcf->variables.nelts - 1;
159
160 return v->index;
161 }
162
163
164 ngx_stream_variable_value_t *
165 ngx_stream_get_indexed_variable(ngx_stream_session_t *s, ngx_uint_t index)
166 {
167 ngx_stream_variable_t *v;
168 ngx_stream_core_main_conf_t *cmcf;
169
170 cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
171
172 if (cmcf->variables.nelts <= index) {
173 ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0,
174 "unknown variable index: %ui", index);
175 return NULL;
176 }
177
178 if (s->variables[index].not_found || s->variables[index].valid) {
179 return &s->variables[index];
180 }
181
182 v = cmcf->variables.elts;
183
184 if (v[index].get_handler(s, &s->variables[index], v[index].data)
185 == NGX_OK)
186 {
187 if (v[index].flags & NGX_STREAM_VAR_NOCACHEABLE) {
188 s->variables[index].no_cacheable = 1;
189 }
190
191 return &s->variables[index];
192 }
193
194 s->variables[index].valid = 0;
195 s->variables[index].not_found = 1;
196
197 return NULL;
198 }
199
200
201 ngx_stream_variable_value_t *
202 ngx_stream_get_flushed_variable(ngx_stream_session_t *s, ngx_uint_t index)
203 {
204 ngx_stream_variable_value_t *v;
205
206 v = &s->variables[index];
207
208 if (v->valid || v->not_found) {
209 if (!v->no_cacheable) {
210 return v;
211 }
212
213 v->valid = 0;
214 v->not_found = 0;
215 }
216
217 return ngx_stream_get_indexed_variable(s, index);
218 }
219
220
221 ngx_stream_variable_value_t *
222 ngx_stream_get_variable(ngx_stream_session_t *s, ngx_str_t *name,
223 ngx_uint_t key)
224 {
225 ngx_stream_variable_t *v;
226 ngx_stream_variable_value_t *vv;
227 ngx_stream_core_main_conf_t *cmcf;
228
229 cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
230
231 v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);
232
233 if (v) {
234 if (v->flags & NGX_STREAM_VAR_INDEXED) {
235 return ngx_stream_get_flushed_variable(s, v->index);
236
237 } else {
238
239 vv = ngx_palloc(s->connection->pool,
240 sizeof(ngx_stream_variable_value_t));
241
242 if (vv && v->get_handler(s, vv, v->data) == NGX_OK) {
243 return vv;
244 }
245
246 return NULL;
247 }
248 }
249
250 vv = ngx_palloc(s->connection->pool, sizeof(ngx_stream_variable_value_t));
251 if (vv == NULL) {
252 return NULL;
253 }
254
255 vv->not_found = 1;
256
257 return vv;
258 }
259
260
261 static ngx_int_t
262 ngx_stream_variable_nginx_version(ngx_stream_session_t *s,
263 ngx_stream_variable_value_t *v, uintptr_t data)
264 {
265 v->len = sizeof(NGINX_VERSION) - 1;
266 v->valid = 1;
267 v->no_cacheable = 0;
268 v->not_found = 0;
269 v->data = (u_char *) NGINX_VERSION;
270
271 return NGX_OK;
272 }
273
274
275 void *
276 ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map,
277 ngx_str_t *match)
278 {
279 void *value;
280 u_char *low;
281 size_t len;
282 ngx_uint_t key;
283
284 len = match->len;
285
286 if (len) {
287 low = ngx_pnalloc(s->connection->pool, len);
288 if (low == NULL) {
289 return NULL;
290 }
291
292 } else {
293 low = NULL;
294 }
295
296 key = ngx_hash_strlow(low, match->data, len);
297
298 value = ngx_hash_find_combined(&map->hash, key, low, len);
299 if (value) {
300 return value;
301 }
302
303 #if (NGX_PCRE)
304
305 if (len && map->nregex) {
306 ngx_int_t n;
307 ngx_uint_t i;
308 ngx_stream_map_regex_t *reg;
309
310 reg = map->regex;
311
312 for (i = 0; i < map->nregex; i++) {
313
314 n = ngx_stream_regex_exec(s, reg[i].regex, match);
315
316 if (n == NGX_OK) {
317 return reg[i].value;
318 }
319
320 if (n == NGX_DECLINED) {
321 continue;
322 }
323
324 /* NGX_ERROR */
325
326 return NULL;
327 }
328 }
329
330 #endif
331
332 return NULL;
333 }
334
335
336 #if (NGX_PCRE)
337
338 static ngx_int_t
339 ngx_stream_variable_not_found(ngx_stream_session_t *s,
340 ngx_stream_variable_value_t *v, uintptr_t data)
341 {
342 v->not_found = 1;
343 return NGX_OK;
344 }
345
346
347 ngx_stream_regex_t *
348 ngx_stream_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc)
349 {
350 u_char *p;
351 size_t size;
352 ngx_str_t name;
353 ngx_uint_t i, n;
354 ngx_stream_variable_t *v;
355 ngx_stream_regex_t *re;
356 ngx_stream_regex_variable_t *rv;
357 ngx_stream_core_main_conf_t *cmcf;
358
359 rc->pool = cf->pool;
360
361 if (ngx_regex_compile(rc) != NGX_OK) {
362 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc->err);
363 return NULL;
364 }
365
366 re = ngx_pcalloc(cf->pool, sizeof(ngx_stream_regex_t));
367 if (re == NULL) {
368 return NULL;
369 }
370
371 re->regex = rc->regex;
372 re->ncaptures = rc->captures;
373 re->name = rc->pattern;
374
375 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
376 cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures);
377
378 n = (ngx_uint_t) rc->named_captures;
379
380 if (n == 0) {
381 return re;
382 }
383
384 rv = ngx_palloc(rc->pool, n * sizeof(ngx_stream_regex_variable_t));
385 if (rv == NULL) {
386 return NULL;
387 }
388
389 re->variables = rv;
390 re->nvariables = n;
391
392 size = rc->name_size;
393 p = rc->names;
394
395 for (i = 0; i < n; i++) {
396 rv[i].capture = 2 * ((p[0] << 8) + p[1]);
397
398 name.data = &p[2];
399 name.len = ngx_strlen(name.data);
400
401 v = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);
402 if (v == NULL) {
403 return NULL;
404 }
405
406 rv[i].index = ngx_stream_get_variable_index(cf, &name);
407 if (rv[i].index == NGX_ERROR) {
408 return NULL;
409 }
410
411 v->get_handler = ngx_stream_variable_not_found;
412
413 p += size;
414 }
415
416 return re;
417 }
418
419
420 ngx_int_t
421 ngx_stream_regex_exec(ngx_stream_session_t *s, ngx_stream_regex_t *re,
422 ngx_str_t *str)
423 {
424 ngx_int_t rc, index;
425 ngx_uint_t i, n, len;
426 ngx_stream_variable_value_t *vv;
427 ngx_stream_core_main_conf_t *cmcf;
428
429 cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
430
431 if (re->ncaptures) {
432 len = cmcf->ncaptures;
433
434 if (s->captures == NULL) {
435 s->captures = ngx_palloc(s->connection->pool, len * sizeof(int));
436 if (s->captures == NULL) {
437 return NGX_ERROR;
438 }
439 }
440
441 } else {
442 len = 0;
443 }
444
445 rc = ngx_regex_exec(re->regex, str, s->captures, len);
446
447 if (rc == NGX_REGEX_NO_MATCHED) {
448 return NGX_DECLINED;
449 }
450
451 if (rc < 0) {
452 ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0,
453 ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
454 rc, str, &re->name);
455 return NGX_ERROR;
456 }
457
458 for (i = 0; i < re->nvariables; i++) {
459
460 n = re->variables[i].capture;
461 index = re->variables[i].index;
462 vv = &s->variables[index];
463
464 vv->len = s->captures[n + 1] - s->captures[n];
465 vv->valid = 1;
466 vv->no_cacheable = 0;
467 vv->not_found = 0;
468 vv->data = &str->data[s->captures[n]];
469
470 #if (NGX_DEBUG)
471 {
472 ngx_stream_variable_t *v;
473
474 v = cmcf->variables.elts;
475
476 ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
477 "stream regex set $%V to \"%v\"", &v[index].name, vv);
478 }
479 #endif
480 }
481
482 s->ncaptures = rc * 2;
483 s->captures_data = str->data;
484
485 return NGX_OK;
486 }
487
488 #endif
489
490
491 ngx_int_t
492 ngx_stream_variables_add_core_vars(ngx_conf_t *cf)
493 {
494 ngx_int_t rc;
495 ngx_stream_variable_t *cv, *v;
496 ngx_stream_core_main_conf_t *cmcf;
497
498 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
499
500 cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,
501 sizeof(ngx_hash_keys_arrays_t));
502 if (cmcf->variables_keys == NULL) {
503 return NGX_ERROR;
504 }
505
506 cmcf->variables_keys->pool = cf->pool;
507 cmcf->variables_keys->temp_pool = cf->pool;
508
509 if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)
510 != NGX_OK)
511 {
512 return NGX_ERROR;
513 }
514
515 for (cv = ngx_stream_core_variables; cv->name.len; cv++) {
516 v = ngx_palloc(cf->pool, sizeof(ngx_stream_variable_t));
517 if (v == NULL) {
518 return NGX_ERROR;
519 }
520
521 *v = *cv;
522
523 rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v,
524 NGX_HASH_READONLY_KEY);
525
526 if (rc == NGX_OK) {
527 continue;
528 }
529
530 if (rc == NGX_BUSY) {
531 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
532 "conflicting variable name \"%V\"", &v->name);
533 }
534
535 return NGX_ERROR;
536 }
537
538 return NGX_OK;
539 }
540
541
542 ngx_int_t
543 ngx_stream_variables_init_vars(ngx_conf_t *cf)
544 {
545 ngx_uint_t i, n;
546 ngx_hash_key_t *key;
547 ngx_hash_init_t hash;
548 ngx_stream_variable_t *v, *av;
549 ngx_stream_core_main_conf_t *cmcf;
550
551 /* set the handlers for the indexed stream variables */
552
553 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
554
555 v = cmcf->variables.elts;
556 key = cmcf->variables_keys->keys.elts;
557
558 for (i = 0; i < cmcf->variables.nelts; i++) {
559
560 for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
561
562 av = key[n].value;
563
564 if (v[i].name.len == key[n].key.len
565 && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len)
566 == 0)
567 {
568 v[i].get_handler = av->get_handler;
569 v[i].data = av->data;
570
571 av->flags |= NGX_STREAM_VAR_INDEXED;
572 v[i].flags = av->flags;
573
574 av->index = i;
575
576 if (av->get_handler == NULL) {
577 break;
578 }
579
580 goto next;
581 }
582 }
583
584 ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
585 "unknown \"%V\" variable", &v[i].name);
586
587 return NGX_ERROR;
588
589 next:
590 continue;
591 }
592
593
594 for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
595 av = key[n].value;
596
597 if (av->flags & NGX_STREAM_VAR_NOHASH) {
598 key[n].key.data = NULL;
599 }
600 }
601
602
603 hash.hash = &cmcf->variables_hash;
604 hash.key = ngx_hash_key;
605 hash.max_size = cmcf->variables_hash_max_size;
606 hash.bucket_size = cmcf->variables_hash_bucket_size;
607 hash.name = "variables_hash";
608 hash.pool = cf->pool;
609 hash.temp_pool = NULL;
610
611 if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts,
612 cmcf->variables_keys->keys.nelts)
613 != NGX_OK)
614 {
615 return NGX_ERROR;
616 }
617
618 cmcf->variables_keys = NULL;
619
620 return NGX_OK;
621 }