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