xref: /unit/src/nxt_var.c (revision 2759:375cdded2f5f)
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