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 void 95 nxt_var_raw(nxt_var_t *var, nxt_str_t *str) 96 { 97 str->length = var->length; 98 str->start = nxt_var_raw_start(var); 99 } 100 101 102 nxt_bool_t 103 nxt_var_is_const(nxt_var_t *var) 104 { 105 return (var->vars == 0); 106 } 107 108 109 static nxt_int_t 110 nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data) 111 { 112 nxt_var_decl_t *decl; 113 114 decl = data; 115 116 return nxt_strstr_eq(&lhq->key, &decl->name) ? NXT_OK : NXT_DECLINED; 117 } 118 119 120 static nxt_var_decl_t * 121 nxt_var_hash_find(nxt_str_t *name) 122 { 123 nxt_lvlhsh_query_t lhq; 124 125 lhq.key_hash = nxt_djb_hash(name->start, name->length); 126 lhq.key = *name; 127 lhq.proto = &nxt_var_hash_proto; 128 129 if (nxt_lvlhsh_find(&nxt_var_hash, &lhq) != NXT_OK) { 130 return NULL; 131 } 132 133 return lhq.value; 134 } 135 136 137 static nxt_int_t 138 nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data) 139 { 140 return NXT_OK; 141 } 142 143 144 static nxt_str_t * 145 nxt_var_cache_find(nxt_lvlhsh_t *lh, uint32_t index) 146 { 147 nxt_lvlhsh_query_t lhq; 148 149 lhq.key_hash = nxt_murmur_hash2_uint32(&index); 150 lhq.key.length = sizeof(uint32_t); 151 lhq.key.start = (u_char *) &index; 152 lhq.proto = &nxt_var_cache_proto; 153 154 if (nxt_lvlhsh_find(lh, &lhq) != NXT_OK) { 155 return NULL; 156 } 157 158 return lhq.value; 159 } 160 161 162 static nxt_int_t 163 nxt_var_cache_add(nxt_lvlhsh_t *lh, uint32_t index, nxt_str_t *value, 164 nxt_mp_t *mp) 165 { 166 nxt_lvlhsh_query_t lhq; 167 168 lhq.key_hash = nxt_murmur_hash2_uint32(&index); 169 lhq.replace = 0; 170 lhq.key.length = sizeof(uint32_t); 171 lhq.key.start = (u_char *) &index; 172 lhq.value = value; 173 lhq.proto = &nxt_var_cache_proto; 174 lhq.pool = mp; 175 176 return nxt_lvlhsh_insert(lh, &lhq); 177 } 178 179 180 nxt_int_t 181 nxt_var_register(nxt_var_decl_t *decl, size_t n) 182 { 183 nxt_uint_t i; 184 nxt_lvlhsh_query_t lhq; 185 186 lhq.replace = 0; 187 lhq.proto = &nxt_var_hash_proto; 188 189 for (i = 0; i < n; i++) { 190 lhq.key = decl[i].name; 191 lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); 192 lhq.value = &decl[i]; 193 194 if (nxt_slow_path(nxt_lvlhsh_insert(&nxt_var_hash, &lhq) != NXT_OK)) { 195 return NXT_ERROR; 196 } 197 } 198 199 nxt_var_count += n; 200 201 return NXT_OK; 202 } 203 204 205 nxt_int_t 206 nxt_var_index_init(void) 207 { 208 nxt_uint_t i; 209 nxt_var_decl_t *decl; 210 nxt_var_handler_t *index; 211 nxt_lvlhsh_each_t lhe; 212 213 index = nxt_memalign(64, nxt_var_count * sizeof(nxt_var_handler_t)); 214 if (index == NULL) { 215 return NXT_ERROR; 216 } 217 218 nxt_lvlhsh_each_init(&lhe, &nxt_var_hash_proto); 219 220 for (i = 0; i < nxt_var_count; i++) { 221 decl = nxt_lvlhsh_each(&nxt_var_hash, &lhe); 222 decl->index = i; 223 index[i] = decl->handler; 224 } 225 226 nxt_var_index = index; 227 228 return NXT_OK; 229 } 230 231 232 nxt_var_t * 233 nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_bool_t strz) 234 { 235 u_char *p, *end, *next, *src; 236 size_t size; 237 nxt_var_t *var; 238 nxt_str_t part; 239 nxt_uint_t n; 240 nxt_bool_t is_var; 241 nxt_var_sub_t *subs; 242 nxt_var_decl_t *decl; 243 244 n = 0; 245 246 p = str->start; 247 end = p + str->length; 248 249 while (p < end) { 250 p = nxt_var_next_part(p, end - p, &part, &is_var); 251 if (nxt_slow_path(p == NULL)) { 252 return NULL; 253 } 254 255 if (is_var) { 256 n++; 257 } 258 } 259 260 size = sizeof(nxt_var_t) + n * sizeof(nxt_var_sub_t) + str->length; 261 262 var = nxt_mp_get(mp, size + strz); 263 if (nxt_slow_path(var == NULL)) { 264 return NULL; 265 } 266 267 var->length = str->length; 268 var->vars = n; 269 var->strz = strz; 270 271 subs = nxt_var_subs(var); 272 src = nxt_var_raw_start(var); 273 274 nxt_memcpy(src, str->start, str->length); 275 276 if (strz) { 277 src[str->length] = '\0'; 278 } 279 280 n = 0; 281 p = str->start; 282 283 while (p < end) { 284 next = nxt_var_next_part(p, end - p, &part, &is_var); 285 286 if (is_var) { 287 decl = nxt_var_hash_find(&part); 288 if (nxt_slow_path(decl == NULL)) { 289 return NULL; 290 } 291 292 subs[n].index = decl->index; 293 subs[n].length = next - p; 294 subs[n].position = p - str->start; 295 296 n++; 297 } 298 299 p = next; 300 } 301 302 return var; 303 } 304 305 306 nxt_int_t 307 nxt_var_test(nxt_str_t *str, u_char *error) 308 { 309 u_char *p, *end, *next; 310 nxt_str_t part; 311 nxt_bool_t is_var; 312 nxt_var_decl_t *decl; 313 314 p = str->start; 315 end = p + str->length; 316 317 while (p < end) { 318 next = nxt_var_next_part(p, end - p, &part, &is_var); 319 320 if (next == NULL) { 321 nxt_sprintf(error, error + NXT_MAX_ERROR_STR, 322 "Invalid variable at position %uz%Z", p - str->start); 323 324 return NXT_ERROR; 325 } 326 327 if (is_var) { 328 decl = nxt_var_hash_find(&part); 329 330 if (decl == NULL) { 331 nxt_sprintf(error, error + NXT_MAX_ERROR_STR, 332 "Unknown variable \"%V\"%Z", &part); 333 334 return NXT_ERROR; 335 } 336 } 337 338 p = next; 339 } 340 341 return NXT_OK; 342 } 343 344 345 static u_char * 346 nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part, 347 nxt_bool_t *is_var) 348 { 349 u_char *p, *end, ch, c; 350 nxt_bool_t bracket; 351 352 end = start + length; 353 354 p = nxt_memchr(start, '$', length); 355 356 if (p == start) { 357 *is_var = 1; 358 359 p++; 360 361 if (p == end) { 362 return NULL; 363 } 364 365 if (*p == '{') { 366 bracket = 1; 367 368 if (end - p < 2) { 369 return NULL; 370 } 371 372 p++; 373 374 } else { 375 bracket = 0; 376 } 377 378 start = p; 379 380 for ( ;; ) { 381 ch = *p; 382 383 c = (u_char) (ch | 0x20); 384 if ((c < 'a' || c > 'z') && ch != '_') { 385 386 if (bracket && ch != '}') { 387 return NULL; 388 } 389 390 break; 391 } 392 393 p++; 394 395 if (p == end) { 396 if (bracket) { 397 return NULL; 398 } 399 400 break; 401 } 402 } 403 404 length = p - start; 405 end = p + bracket; 406 407 } else { 408 *is_var = 0; 409 410 if (p != NULL) { 411 length = p - start; 412 end = p; 413 } 414 } 415 416 part->length = length; 417 part->start = start; 418 419 return end; 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