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