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