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