1
2 /*
3 * Copyright (C) NGINX, Inc.
4 */
5
6 #include <nxt_main.h>
7
8
9 struct nxt_var_s {
10 size_t length;
11 nxt_uint_t vars;
12 nxt_var_flags_t flags;
13 u_char data[];
14
15 /*
16 nxt_var_sub_t subs[vars];
17 u_char raw[length];
18 */
19 };
20
21
22 typedef struct {
23 uint32_t index;
24 uint32_t length;
25 uint32_t position;
26 } nxt_var_sub_t;
27
28
29 struct nxt_var_query_s {
30 nxt_mp_t *pool;
31
32 nxt_lvlhsh_t cache;
33 nxt_str_t *spare;
34
35 nxt_uint_t waiting;
36 nxt_uint_t failed; /* 1 bit */
37
38 void *ctx;
39 void *data;
40
41 nxt_work_handler_t ready;
42 nxt_work_handler_t error;
43 };
44
45
46 #define nxt_var_subs(var) ((nxt_var_sub_t *) (var)->data)
47
48 #define nxt_var_raw_start(var) \
49 ((var)->data + (var)->vars * sizeof(nxt_var_sub_t))
50
51
52 static nxt_int_t nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
53 static nxt_var_decl_t *nxt_var_hash_find(nxt_str_t *name);
54
55 static nxt_var_decl_t *nxt_var_decl_get(nxt_str_t *name, nxt_array_t *fields,
56 uint32_t *index);
57 static nxt_var_field_t *nxt_var_field_add(nxt_array_t *fields, nxt_str_t *name,
58 uint32_t hash);
59
60 static nxt_int_t nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data);
61 static nxt_str_t *nxt_var_cache_value(nxt_task_t *task, nxt_var_query_t *query,
62 uint32_t index);
63
64 static u_char *nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part,
65 nxt_bool_t *is_var);
66
67
68 static const nxt_lvlhsh_proto_t nxt_var_hash_proto nxt_aligned(64) = {
69 NXT_LVLHSH_DEFAULT,
70 nxt_var_hash_test,
71 nxt_lvlhsh_alloc,
72 nxt_lvlhsh_free,
73 };
74
75 static const nxt_lvlhsh_proto_t nxt_var_cache_proto nxt_aligned(64) = {
76 NXT_LVLHSH_DEFAULT,
77 nxt_var_cache_test,
78 nxt_mp_lvlhsh_alloc,
79 nxt_mp_lvlhsh_free,
80 };
81
82
83 static nxt_lvlhsh_t nxt_var_hash;
84 static uint32_t nxt_var_count;
85
86 static nxt_var_handler_t *nxt_var_index;
87
88
89 static nxt_int_t
nxt_var_hash_test(nxt_lvlhsh_query_t * lhq,void * data)90 nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
91 {
92 nxt_var_decl_t *decl;
93
94 decl = data;
95
96 return nxt_strstr_eq(&lhq->key, &decl->name) ? NXT_OK : NXT_DECLINED;
97 }
98
99
100 static nxt_var_decl_t *
nxt_var_hash_find(nxt_str_t * name)101 nxt_var_hash_find(nxt_str_t *name)
102 {
103 nxt_lvlhsh_query_t lhq;
104
105 lhq.key_hash = nxt_djb_hash(name->start, name->length);
106 lhq.key = *name;
107 lhq.proto = &nxt_var_hash_proto;
108
109 if (nxt_lvlhsh_find(&nxt_var_hash, &lhq) != NXT_OK) {
110 return NULL;
111 }
112
113 return lhq.value;
114 }
115
116
117 static nxt_var_decl_t *
nxt_var_decl_get(nxt_str_t * name,nxt_array_t * fields,uint32_t * index)118 nxt_var_decl_get(nxt_str_t *name, nxt_array_t *fields, uint32_t *index)
119 {
120 u_char *p, *end;
121 int64_t hash;
122 uint16_t field;
123 nxt_str_t str;
124 nxt_var_decl_t *decl;
125 nxt_var_field_t *f;
126
127 f = NULL;
128 field = 0;
129 decl = nxt_var_hash_find(name);
130
131 if (decl == NULL) {
132 p = name->start;
133 end = p + name->length;
134
135 while (p < end) {
136 if (*p++ == '_') {
137 break;
138 }
139 }
140
141 if (p == end) {
142 return NULL;
143 }
144
145 str.start = name->start;
146 str.length = p - 1 - name->start;
147
148 decl = nxt_var_hash_find(&str);
149
150 if (decl != NULL) {
151 str.start = p;
152 str.length = end - p;
153
154 hash = decl->field_hash(fields->mem_pool, &str);
155 if (nxt_slow_path(hash == -1)) {
156 return NULL;
157 }
158
159 f = nxt_var_field_add(fields, &str, (uint32_t) hash);
160 if (nxt_slow_path(f == NULL)) {
161 return NULL;
162 }
163
164 field = f->index;
165 }
166 }
167
168 if (decl != NULL) {
169 if (decl->field_hash != NULL && f == NULL) {
170 return NULL;
171 }
172
173 if (index != NULL) {
174 *index = (decl->index << 16) | field;
175 }
176 }
177
178 return decl;
179 }
180
181
182 static nxt_var_field_t *
nxt_var_field_add(nxt_array_t * fields,nxt_str_t * name,uint32_t hash)183 nxt_var_field_add(nxt_array_t *fields, nxt_str_t *name, uint32_t hash)
184 {
185 nxt_uint_t i;
186 nxt_var_field_t *field;
187
188 field = fields->elts;
189
190 for (i = 0; i < fields->nelts; i++) {
191 if (field[i].hash == hash
192 && nxt_strstr_eq(&field[i].name, name))
193 {
194 return field;
195 }
196 }
197
198 field = nxt_array_add(fields);
199 if (nxt_slow_path(field == NULL)) {
200 return NULL;
201 }
202
203 field->name = *name;
204 field->hash = hash;
205 field->index = fields->nelts - 1;
206
207 return field;
208 }
209
210
211 nxt_var_field_t *
nxt_var_field_get(nxt_array_t * fields,uint16_t index)212 nxt_var_field_get(nxt_array_t *fields, uint16_t index)
213 {
214 nxt_uint_t nfields;
215 nxt_var_field_t *field;
216
217 field = fields->elts;
218 nfields = fields->nelts;
219
220 if (nfields > 0 && index <= nfields) {
221 return &field[index];
222 }
223
224 return NULL;
225 }
226
227
228 static nxt_int_t
nxt_var_cache_test(nxt_lvlhsh_query_t * lhq,void * data)229 nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data)
230 {
231 return NXT_OK;
232 }
233
234
235 static nxt_str_t *
nxt_var_cache_value(nxt_task_t * task,nxt_var_query_t * query,uint32_t index)236 nxt_var_cache_value(nxt_task_t *task, nxt_var_query_t *query, uint32_t index)
237 {
238 nxt_int_t ret;
239 nxt_str_t *value;
240 nxt_lvlhsh_query_t lhq;
241
242 value = query->spare;
243
244 if (value == NULL) {
245 value = nxt_mp_zget(query->pool, sizeof(nxt_str_t));
246 if (nxt_slow_path(value == NULL)) {
247 return NULL;
248 }
249
250 query->spare = value;
251 }
252
253 lhq.key_hash = nxt_murmur_hash2_uint32(&index);
254 lhq.replace = 0;
255 lhq.key.length = sizeof(uint32_t);
256 lhq.key.start = (u_char *) &index;
257 lhq.value = value;
258 lhq.proto = &nxt_var_cache_proto;
259 lhq.pool = query->pool;
260
261 ret = nxt_lvlhsh_insert(&query->cache, &lhq);
262 if (nxt_slow_path(ret == NXT_ERROR)) {
263 return NULL;
264 }
265
266 if (ret == NXT_OK) {
267 ret = nxt_var_index[index >> 16](task, value, query->ctx,
268 index & 0xffff);
269 if (nxt_slow_path(ret != NXT_OK)) {
270 return NULL;
271 }
272
273 query->spare = NULL;
274 }
275
276 return lhq.value;
277 }
278
279
280 nxt_int_t
nxt_var_register(nxt_var_decl_t * decl,size_t n)281 nxt_var_register(nxt_var_decl_t *decl, size_t n)
282 {
283 nxt_uint_t i;
284 nxt_lvlhsh_query_t lhq;
285
286 lhq.replace = 0;
287 lhq.proto = &nxt_var_hash_proto;
288
289 for (i = 0; i < n; i++) {
290 lhq.key = decl[i].name;
291 lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
292 lhq.value = &decl[i];
293
294 if (nxt_slow_path(nxt_lvlhsh_insert(&nxt_var_hash, &lhq) != NXT_OK)) {
295 return NXT_ERROR;
296 }
297 }
298
299 nxt_var_count += n;
300
301 return NXT_OK;
302 }
303
304
305 nxt_int_t
nxt_var_index_init(void)306 nxt_var_index_init(void)
307 {
308 nxt_uint_t i;
309 nxt_var_decl_t *decl;
310 nxt_var_handler_t *index;
311 nxt_lvlhsh_each_t lhe;
312
313 index = nxt_memalign(64, nxt_var_count * sizeof(nxt_var_handler_t));
314 if (index == NULL) {
315 return NXT_ERROR;
316 }
317
318 nxt_lvlhsh_each_init(&lhe, &nxt_var_hash_proto);
319
320 for (i = 0; i < nxt_var_count; i++) {
321 decl = nxt_lvlhsh_each(&nxt_var_hash, &lhe);
322 decl->index = i;
323 index[i] = decl->handler;
324 }
325
326 nxt_var_index = index;
327
328 return NXT_OK;
329 }
330
331
332 nxt_var_t *
nxt_var_compile(nxt_str_t * str,nxt_mp_t * mp,nxt_array_t * fields,nxt_var_flags_t flags)333 nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields,
334 nxt_var_flags_t flags)
335 {
336 u_char *p, *end, *next, *src;
337 size_t size;
338 uint32_t index;
339 nxt_bool_t strz;
340 nxt_var_t *var;
341 nxt_str_t part;
342 nxt_uint_t n;
343 nxt_bool_t is_var;
344 nxt_var_sub_t *subs;
345 nxt_var_decl_t *decl;
346
347 strz = (flags & NXT_VAR_STRZ) != 0;
348
349 n = 0;
350
351 p = str->start;
352 end = p + str->length;
353
354 while (p < end) {
355 p = nxt_var_next_part(p, end - p, &part, &is_var);
356 if (nxt_slow_path(p == NULL)) {
357 return NULL;
358 }
359
360 if (is_var) {
361 n++;
362 }
363 }
364
365 size = sizeof(nxt_var_t) + n * sizeof(nxt_var_sub_t) + str->length;
366
367 var = nxt_mp_get(mp, size + strz);
368 if (nxt_slow_path(var == NULL)) {
369 return NULL;
370 }
371
372 var->length = str->length;
373 var->vars = n;
374 var->flags = flags;
375
376 subs = nxt_var_subs(var);
377 src = nxt_var_raw_start(var);
378
379 nxt_memcpy(src, str->start, str->length);
380
381 if (strz) {
382 src[str->length] = '\0';
383 }
384
385 n = 0;
386 p = str->start;
387
388 while (p < end) {
389 next = nxt_var_next_part(p, end - p, &part, &is_var);
390
391 if (is_var) {
392 decl = nxt_var_decl_get(&part, fields, &index);
393 if (nxt_slow_path(decl == NULL)) {
394 return NULL;
395 }
396
397 subs[n].index = index;
398 subs[n].length = next - p;
399 subs[n].position = p - str->start;
400
401 n++;
402 }
403
404 p = next;
405 }
406
407 return var;
408 }
409
410
411 nxt_int_t
nxt_var_test(nxt_str_t * str,nxt_array_t * fields,u_char * error)412 nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error)
413 {
414 u_char *p, *end, *next;
415 nxt_str_t part;
416 nxt_bool_t is_var;
417 nxt_var_decl_t *decl;
418
419 p = str->start;
420 end = p + str->length;
421
422 while (p < end) {
423 next = nxt_var_next_part(p, end - p, &part, &is_var);
424
425 if (next == NULL) {
426 nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
427 "Invalid variable at position %uz%Z", p - str->start);
428
429 return NXT_ERROR;
430 }
431
432 if (is_var) {
433 decl = nxt_var_decl_get(&part, fields, NULL);
434
435 if (decl == NULL) {
436 nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
437 "Unknown variable \"%V\"%Z", &part);
438
439 return NXT_ERROR;
440 }
441 }
442
443 p = next;
444 }
445
446 return NXT_OK;
447 }
448
449
450 static u_char *
nxt_var_next_part(u_char * start,size_t length,nxt_str_t * part,nxt_bool_t * is_var)451 nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part,
452 nxt_bool_t *is_var)
453 {
454 u_char *p, *end, ch, c;
455 nxt_bool_t bracket;
456
457 end = start + length;
458
459 p = nxt_memchr(start, '$', length);
460
461 if (p == start) {
462 *is_var = 1;
463
464 p++;
465
466 if (p == end) {
467 return NULL;
468 }
469
470 if (*p == '{') {
471 bracket = 1;
472
473 if (end - p < 2) {
474 return NULL;
475 }
476
477 p++;
478
479 } else {
480 bracket = 0;
481 }
482
483 start = p;
484
485 for ( ;; ) {
486 ch = *p;
487
488 c = (u_char) (ch | 0x20);
489 if ((c < 'a' || c > 'z') && ch != '_') {
490
491 if (bracket && ch != '}') {
492 return NULL;
493 }
494
495 break;
496 }
497
498 p++;
499
500 if (p == end) {
501 if (bracket) {
502 return NULL;
503 }
504
505 break;
506 }
507 }
508
509 length = p - start;
510 end = p + bracket;
511
512 } else {
513 *is_var = 0;
514
515 if (p != NULL) {
516 length = p - start;
517 end = p;
518 }
519 }
520
521 part->length = length;
522 part->start = start;
523
524 return end;
525 }
526
527
528 inline void
nxt_var_raw(nxt_var_t * var,nxt_str_t * str)529 nxt_var_raw(nxt_var_t *var, nxt_str_t *str)
530 {
531 str->length = var->length;
532 str->start = nxt_var_raw_start(var);
533 }
534
535
536 inline nxt_bool_t
nxt_var_is_const(nxt_var_t * var)537 nxt_var_is_const(nxt_var_t *var)
538 {
539 return (var->vars == 0);
540 }
541
542
543 nxt_int_t
nxt_var_query_init(nxt_var_query_t ** query_p,void * ctx,nxt_mp_t * mp)544 nxt_var_query_init(nxt_var_query_t **query_p, void *ctx, nxt_mp_t *mp)
545 {
546 nxt_var_query_t *query;
547
548 query = *query_p;
549
550 if (*query_p == NULL) {
551 query = nxt_mp_zget(mp, sizeof(nxt_var_query_t));
552 if (nxt_slow_path(query == NULL)) {
553 return NXT_ERROR;
554 }
555 }
556
557 query->pool = mp;
558 query->ctx = ctx;
559
560 *query_p = query;
561
562 return NXT_OK;
563 }
564
565
566 void
nxt_var_query(nxt_task_t * task,nxt_var_query_t * query,nxt_var_t * var,nxt_str_t * str)567 nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var,
568 nxt_str_t *str)
569 {
570 u_char *p, *src;
571 size_t length, last, next;
572 nxt_str_t *value, **part;
573 nxt_uint_t i;
574 nxt_bool_t strz, logging;
575 nxt_array_t parts;
576 nxt_var_sub_t *subs;
577
578 if (nxt_var_is_const(var)) {
579 nxt_var_raw(var, str);
580 return;
581 }
582
583 if (nxt_slow_path(query->failed)) {
584 return;
585 }
586
587 nxt_memzero(&parts, sizeof(nxt_array_t));
588 nxt_array_init(&parts, query->pool, sizeof(nxt_str_t *));
589
590 strz = (var->flags & NXT_VAR_STRZ) != 0;
591 logging = (var->flags & NXT_VAR_LOGGING) != 0;
592
593 subs = nxt_var_subs(var);
594
595 length = var->length;
596
597 for (i = 0; i < var->vars; i++) {
598 value = nxt_var_cache_value(task, query, subs[i].index);
599 if (nxt_slow_path(value == NULL)) {
600 goto fail;
601 }
602
603 part = nxt_array_add(&parts);
604 if (nxt_slow_path(part == NULL)) {
605 goto fail;
606 }
607
608 *part = value;
609
610 length += value->length - subs[i].length;
611
612 if (logging && value->start == NULL) {
613 length += 1;
614 }
615 }
616
617 p = nxt_mp_nget(query->pool, length + strz);
618 if (nxt_slow_path(p == NULL)) {
619 goto fail;
620 }
621
622 str->length = length;
623 str->start = p;
624
625 part = parts.elts;
626 src = nxt_var_raw_start(var);
627
628 last = 0;
629
630 for (i = 0; i < var->vars; i++) {
631 next = subs[i].position;
632
633 if (next != last) {
634 p = nxt_cpymem(p, &src[last], next - last);
635 }
636
637 p = nxt_cpymem(p, part[i]->start, part[i]->length);
638
639 if (logging && part[i]->start == NULL) {
640 *p++ = '-';
641 }
642
643 last = next + subs[i].length;
644 }
645
646 if (last != var->length) {
647 p = nxt_cpymem(p, &src[last], var->length - last);
648 }
649
650 if (strz) {
651 *p = '\0';
652 }
653
654 nxt_debug(task, "var: \"%*s\" -> \"%V\"", length, src, str);
655
656 return;
657
658 fail:
659
660 query->failed = 1;
661 }
662
663
664 void
nxt_var_query_resolve(nxt_task_t * task,nxt_var_query_t * query,void * data,nxt_work_handler_t ready,nxt_work_handler_t error)665 nxt_var_query_resolve(nxt_task_t *task, nxt_var_query_t *query, void *data,
666 nxt_work_handler_t ready, nxt_work_handler_t error)
667 {
668 query->data = data;
669 query->ready = ready;
670 query->error = error;
671
672 if (query->waiting == 0) {
673 nxt_work_queue_add(&task->thread->engine->fast_work_queue,
674 query->failed ? query->error : query->ready,
675 task, query->ctx, query->data);
676 }
677 }
678
679
680 void
nxt_var_query_handle(nxt_task_t * task,nxt_var_query_t * query,nxt_bool_t failed)681 nxt_var_query_handle(nxt_task_t *task, nxt_var_query_t *query,
682 nxt_bool_t failed)
683 {
684 query->failed |= failed;
685
686 if (--query->waiting == 0) {
687 nxt_work_queue_add(&task->thread->engine->fast_work_queue,
688 query->failed ? query->error : query->ready,
689 task, query->ctx, query->data);
690 }
691 }
692