Coverage Report

Created: 2025-08-26 07:04

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