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