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 void
nxt_var_raw(nxt_var_t * var,nxt_str_t * str)95 nxt_var_raw(nxt_var_t *var, nxt_str_t *str)
96 {
97 str->length = var->length;
98 str->start = nxt_var_raw_start(var);
99 }
100
101
102 nxt_bool_t
nxt_var_is_const(nxt_var_t * var)103 nxt_var_is_const(nxt_var_t *var)
104 {
105 return (var->vars == 0);
106 }
107
108
109 static nxt_int_t
nxt_var_hash_test(nxt_lvlhsh_query_t * lhq,void * data)110 nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
111 {
112 nxt_var_decl_t *decl;
113
114 decl = data;
115
116 return nxt_strstr_eq(&lhq->key, &decl->name) ? NXT_OK : NXT_DECLINED;
117 }
118
119
120 static nxt_var_decl_t *
nxt_var_hash_find(nxt_str_t * name)121 nxt_var_hash_find(nxt_str_t *name)
122 {
123 nxt_lvlhsh_query_t lhq;
124
125 lhq.key_hash = nxt_djb_hash(name->start, name->length);
126 lhq.key = *name;
127 lhq.proto = &nxt_var_hash_proto;
128
129 if (nxt_lvlhsh_find(&nxt_var_hash, &lhq) != NXT_OK) {
130 return NULL;
131 }
132
133 return lhq.value;
134 }
135
136
137 static nxt_int_t
nxt_var_cache_test(nxt_lvlhsh_query_t * lhq,void * data)138 nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data)
139 {
140 return NXT_OK;
141 }
142
143
144 static nxt_str_t *
nxt_var_cache_find(nxt_lvlhsh_t * lh,uint32_t index)145 nxt_var_cache_find(nxt_lvlhsh_t *lh, uint32_t index)
146 {
147 nxt_lvlhsh_query_t lhq;
148
149 lhq.key_hash = nxt_murmur_hash2_uint32(&index);
150 lhq.key.length = sizeof(uint32_t);
151 lhq.key.start = (u_char *) &index;
152 lhq.proto = &nxt_var_cache_proto;
153
154 if (nxt_lvlhsh_find(lh, &lhq) != NXT_OK) {
155 return NULL;
156 }
157
158 return lhq.value;
159 }
160
161
162 static nxt_int_t
nxt_var_cache_add(nxt_lvlhsh_t * lh,uint32_t index,nxt_str_t * value,nxt_mp_t * mp)163 nxt_var_cache_add(nxt_lvlhsh_t *lh, uint32_t index, nxt_str_t *value,
164 nxt_mp_t *mp)
165 {
166 nxt_lvlhsh_query_t lhq;
167
168 lhq.key_hash = nxt_murmur_hash2_uint32(&index);
169 lhq.replace = 0;
170 lhq.key.length = sizeof(uint32_t);
171 lhq.key.start = (u_char *) &index;
172 lhq.value = value;
173 lhq.proto = &nxt_var_cache_proto;
174 lhq.pool = mp;
175
176 return nxt_lvlhsh_insert(lh, &lhq);
177 }
178
179
180 nxt_int_t
nxt_var_register(nxt_var_decl_t * decl,size_t n)181 nxt_var_register(nxt_var_decl_t *decl, size_t n)
182 {
183 nxt_uint_t i;
184 nxt_lvlhsh_query_t lhq;
185
186 lhq.replace = 0;
187 lhq.proto = &nxt_var_hash_proto;
188
189 for (i = 0; i < n; i++) {
190 lhq.key = decl[i].name;
191 lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
192 lhq.value = &decl[i];
193
194 if (nxt_slow_path(nxt_lvlhsh_insert(&nxt_var_hash, &lhq) != NXT_OK)) {
195 return NXT_ERROR;
196 }
197 }
198
199 nxt_var_count += n;
200
201 return NXT_OK;
202 }
203
204
205 nxt_int_t
nxt_var_index_init(void)206 nxt_var_index_init(void)
207 {
208 nxt_uint_t i;
209 nxt_var_decl_t *decl;
210 nxt_var_handler_t *index;
211 nxt_lvlhsh_each_t lhe;
212
213 index = nxt_memalign(64, nxt_var_count * sizeof(nxt_var_handler_t));
214 if (index == NULL) {
215 return NXT_ERROR;
216 }
217
218 nxt_lvlhsh_each_init(&lhe, &nxt_var_hash_proto);
219
220 for (i = 0; i < nxt_var_count; i++) {
221 decl = nxt_lvlhsh_each(&nxt_var_hash, &lhe);
222 decl->index = i;
223 index[i] = decl->handler;
224 }
225
226 nxt_var_index = index;
227
228 return NXT_OK;
229 }
230
231
232 nxt_var_t *
nxt_var_compile(nxt_str_t * str,nxt_mp_t * mp,nxt_bool_t strz)233 nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_bool_t strz)
234 {
235 u_char *p, *end, *next, *src;
236 size_t size;
237 nxt_var_t *var;
238 nxt_str_t part;
239 nxt_uint_t n;
240 nxt_bool_t is_var;
241 nxt_var_sub_t *subs;
242 nxt_var_decl_t *decl;
243
244 n = 0;
245
246 p = str->start;
247 end = p + str->length;
248
249 while (p < end) {
250 p = nxt_var_next_part(p, end - p, &part, &is_var);
251 if (nxt_slow_path(p == NULL)) {
252 return NULL;
253 }
254
255 if (is_var) {
256 n++;
257 }
258 }
259
260 size = sizeof(nxt_var_t) + n * sizeof(nxt_var_sub_t) + str->length;
261
262 var = nxt_mp_get(mp, size + strz);
263 if (nxt_slow_path(var == NULL)) {
264 return NULL;
265 }
266
267 var->length = str->length;
268 var->vars = n;
269 var->strz = strz;
270
271 subs = nxt_var_subs(var);
272 src = nxt_var_raw_start(var);
273
274 nxt_memcpy(src, str->start, str->length);
275
276 if (strz) {
277 src[str->length] = '\0';
278 }
279
280 n = 0;
281 p = str->start;
282
283 while (p < end) {
284 next = nxt_var_next_part(p, end - p, &part, &is_var);
285
286 if (is_var) {
287 decl = nxt_var_hash_find(&part);
288 if (nxt_slow_path(decl == NULL)) {
289 return NULL;
290 }
291
292 subs[n].index = decl->index;
293 subs[n].length = next - p;
294 subs[n].position = p - str->start;
295
296 n++;
297 }
298
299 p = next;
300 }
301
302 return var;
303 }
304
305
306 nxt_int_t
nxt_var_test(nxt_str_t * str,u_char * error)307 nxt_var_test(nxt_str_t *str, u_char *error)
308 {
309 u_char *p, *end, *next;
310 nxt_str_t part;
311 nxt_bool_t is_var;
312 nxt_var_decl_t *decl;
313
314 p = str->start;
315 end = p + str->length;
316
317 while (p < end) {
318 next = nxt_var_next_part(p, end - p, &part, &is_var);
319
320 if (next == NULL) {
321 nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
322 "Invalid variable at position %uz%Z", p - str->start);
323
324 return NXT_ERROR;
325 }
326
327 if (is_var) {
328 decl = nxt_var_hash_find(&part);
329
330 if (decl == NULL) {
331 nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
332 "Unknown variable \"%V\"%Z", &part);
333
334 return NXT_ERROR;
335 }
336 }
337
338 p = next;
339 }
340
341 return NXT_OK;
342 }
343
344
345 static u_char *
nxt_var_next_part(u_char * start,size_t length,nxt_str_t * part,nxt_bool_t * is_var)346 nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part,
347 nxt_bool_t *is_var)
348 {
349 u_char *p, *end, ch, c;
350 nxt_bool_t bracket;
351
352 end = start + length;
353
354 p = nxt_memchr(start, '$', length);
355
356 if (p == start) {
357 *is_var = 1;
358
359 p++;
360
361 if (p == end) {
362 return NULL;
363 }
364
365 if (*p == '{') {
366 bracket = 1;
367
368 if (end - p < 2) {
369 return NULL;
370 }
371
372 p++;
373
374 } else {
375 bracket = 0;
376 }
377
378 start = p;
379
380 for ( ;; ) {
381 ch = *p;
382
383 c = (u_char) (ch | 0x20);
384 if ((c < 'a' || c > 'z') && ch != '_') {
385
386 if (bracket && ch != '}') {
387 return NULL;
388 }
389
390 break;
391 }
392
393 p++;
394
395 if (p == end) {
396 if (bracket) {
397 return NULL;
398 }
399
400 break;
401 }
402 }
403
404 length = p - start;
405 end = p + bracket;
406
407 } else {
408 *is_var = 0;
409
410 if (p != NULL) {
411 length = p - start;
412 end = p;
413 }
414 }
415
416 part->length = length;
417 part->start = start;
418
419 return end;
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, query, value, query->ctx);
498
499 value = NULL;
500
501 if (ret != NXT_OK) {
502 if (nxt_slow_path(ret != NXT_AGAIN)) {
503 goto fail;
504 }
505
506 query->waiting++;
507 }
508 }
509
510 query->spare = value;
511
512 val = nxt_array_add(&query->values);
513 if (nxt_slow_path(val == NULL)) {
514 goto fail;
515 }
516
517 val->var = var;
518 val->value = str;
519
520 return;
521
522 fail:
523
524 query->failed = 1;
525 }
526
527
528 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)529 nxt_var_query_resolve(nxt_task_t *task, nxt_var_query_t *query, void *data,
530 nxt_work_handler_t ready, nxt_work_handler_t error)
531 {
532 query->data = data;
533 query->ready = ready;
534 query->error = error;
535
536 if (query->waiting == 0) {
537 nxt_var_query_finish(task, query);
538 }
539 }
540
541
542 void
nxt_var_query_handle(nxt_task_t * task,nxt_var_query_t * query,nxt_bool_t failed)543 nxt_var_query_handle(nxt_task_t *task, nxt_var_query_t *query,
544 nxt_bool_t failed)
545 {
546 query->failed |= failed;
547
548 if (--query->waiting == 0) {
549 nxt_var_query_finish(task, query);
550 }
551 }
552
553
554 static void
nxt_var_query_finish(nxt_task_t * task,nxt_var_query_t * query)555 nxt_var_query_finish(nxt_task_t *task, nxt_var_query_t *query)
556 {
557 u_char *p, *src;
558 size_t length, last, next;
559 nxt_str_t *str, **part;
560 nxt_var_t *var;
561 nxt_uint_t i, j;
562 nxt_var_sub_t *subs;
563 nxt_var_value_t *val;
564
565 if (query->failed) {
566 goto done;
567 }
568
569 val = query->values.elts;
570
571 for (i = 0; i < query->values.nelts; i++) {
572 var = val[i].var;
573
574 subs = nxt_var_subs(var);
575 length = var->length;
576
577 for (j = 0; j < var->vars; j++) {
578 str = nxt_var_cache_find(&query->cache, subs[j].index);
579
580 nxt_assert(str != NULL);
581
582 part = nxt_array_add(&query->parts);
583
584 if (nxt_slow_path(part == NULL)) {
585 query->failed = 1;
586 goto done;
587 }
588
589 *part = str;
590
591 length += str->length - subs[j].length;
592 }
593
594 p = nxt_mp_nget(query->values.mem_pool, length + var->strz);
595 if (nxt_slow_path(p == NULL)) {
596 query->failed = 1;
597 goto done;
598 }
599
600 val[i].value->length = length;
601 val[i].value->start = p;
602
603 part = query->parts.elts;
604 src = nxt_var_raw_start(var);
605
606 last = 0;
607
608 for (j = 0; j < var->vars; j++) {
609 next = subs[j].position;
610
611 if (next != last) {
612 p = nxt_cpymem(p, &src[last], next - last);
613 }
614
615 p = nxt_cpymem(p, part[j]->start, part[j]->length);
616
617 last = next + subs[j].length;
618 }
619
620 if (last != var->length) {
621 p = nxt_cpymem(p, &src[last], var->length - last);
622 }
623
624 if (var->strz) {
625 *p = '\0';
626 }
627
628 nxt_array_reset(&query->parts);
629
630 nxt_debug(task, "var: \"%*s\" -> \"%V\"", length, src, val[i].value);
631 }
632
633 done:
634
635 nxt_work_queue_add(&task->thread->engine->fast_work_queue,
636 query->failed ? query->error : query->ready,
637 task, query->ctx, query->data);
638 }
639