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