1 2 /* 3 * Copyright (C) Max Romanov 4 * Copyright (C) Igor Sysoev 5 * Copyright (C) Valentin V. Bartenev 6 * Copyright (C) NGINX, Inc. 7 */ 8 9 #include <nxt_main.h> 10 #include <nxt_runtime.h> 11 #include <nxt_main_process.h> 12 #include <nxt_router.h> 13 #include <nxt_http.h> 14 #include <nxt_application.h> 15 16 #include <glob.h> 17 18 19 typedef struct { 20 nxt_app_type_t type; 21 nxt_str_t version; 22 nxt_str_t file; 23 } nxt_module_t; 24 25 26 static nxt_buf_t *nxt_discovery_modules(nxt_task_t *task, const char *path); 27 static nxt_int_t nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, 28 nxt_array_t *modules, const char *name); 29 static void nxt_discovery_completion_handler(nxt_task_t *task, void *obj, 30 void *data); 31 static void nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, 32 void *data); 33 static nxt_app_module_t *nxt_app_module_load(nxt_task_t *task, 34 const char *name); 35 36 static void nxt_app_http_release(nxt_task_t *task, void *obj, void *data); 37 38 39 static uint32_t compat[] = { 40 NXT_VERNUM, NXT_DEBUG, 41 }; 42 43 44 nxt_str_t nxt_server = nxt_string(NXT_SERVER); 45 46 47 static nxt_thread_mutex_t nxt_app_mutex; 48 static nxt_thread_cond_t nxt_app_cond; 49 50 static nxt_application_module_t *nxt_app; 51 52 53 nxt_int_t 54 nxt_discovery_start(nxt_task_t *task, void *data) 55 { 56 uint32_t stream; 57 nxt_buf_t *b; 58 nxt_int_t ret; 59 nxt_port_t *main_port, *discovery_port; 60 nxt_runtime_t *rt; 61 62 nxt_debug(task, "DISCOVERY"); 63 64 rt = task->thread->runtime; 65 66 b = nxt_discovery_modules(task, rt->modules); 67 if (nxt_slow_path(b == NULL)) { 68 return NXT_ERROR; 69 } 70 71 main_port = rt->port_by_type[NXT_PROCESS_MAIN]; 72 discovery_port = rt->port_by_type[NXT_PROCESS_DISCOVERY]; 73 74 stream = nxt_port_rpc_register_handler(task, discovery_port, 75 nxt_discovery_quit, 76 nxt_discovery_quit, 77 main_port->pid, NULL); 78 79 if (nxt_slow_path(stream == 0)) { 80 return NXT_ERROR; 81 } 82 83 ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_MODULES, -1, 84 stream, discovery_port->id, b); 85 86 if (nxt_slow_path(ret != NXT_OK)) { 87 nxt_port_rpc_cancel(task, discovery_port, stream); 88 return NXT_ERROR; 89 } 90 91 return NXT_OK; 92 } 93 94 95 static nxt_buf_t * 96 nxt_discovery_modules(nxt_task_t *task, const char *path) 97 { 98 char *name; 99 u_char *p, *end; 100 size_t size; 101 glob_t glb; 102 nxt_mp_t *mp; 103 nxt_buf_t *b; 104 nxt_int_t ret; 105 nxt_uint_t i, n; 106 nxt_array_t *modules; 107 nxt_module_t *module; 108 109 b = NULL; 110 111 mp = nxt_mp_create(1024, 128, 256, 32); 112 if (mp == NULL) { 113 return b; 114 } 115 116 ret = glob(path, 0, NULL, &glb); 117 118 n = glb.gl_pathc; 119 120 if (ret != 0) { 121 nxt_log(task, NXT_LOG_NOTICE, 122 "no modules matching: \"%s\" found", path); 123 n = 0; 124 } 125 126 modules = nxt_array_create(mp, n, sizeof(nxt_module_t)); 127 if (modules == NULL) { 128 goto fail; 129 } 130 131 for (i = 0; i < n; i++) { 132 name = glb.gl_pathv[i]; 133 134 ret = nxt_discovery_module(task, mp, modules, name); 135 if (ret != NXT_OK) { 136 goto fail; 137 } 138 } 139 140 size = sizeof("[]") - 1; 141 module = modules->elts; 142 n = modules->nelts; 143 144 for (i = 0; i < n; i++) { 145 nxt_debug(task, "module: %d %V %V", 146 module[i].type, &module[i].version, &module[i].file); 147 148 size += sizeof("{\"type\": ,") - 1; 149 size += sizeof(" \"version\": \"\",") - 1; 150 size += sizeof(" \"file\": \"\"},") - 1; 151 152 size += NXT_INT_T_LEN 153 + module[i].version.length 154 + module[i].file.length; 155 } 156 157 b = nxt_buf_mem_alloc(mp, size, 0); 158 if (b == NULL) { 159 goto fail; 160 } 161 162 b->completion_handler = nxt_discovery_completion_handler; 163 164 p = b->mem.free; 165 end = b->mem.end; 166 *p++ = '['; 167 168 for (i = 0; i < n; i++) { 169 p = nxt_sprintf(p, end, 170 "{\"type\": %d, \"version\": \"%V\", \"file\": \"%V\"},", 171 module[i].type, &module[i].version, &module[i].file); 172 } 173 174 *p++ = ']'; 175 b->mem.free = p; 176 177 fail: 178 179 globfree(&glb); 180 181 return b; 182 } 183 184 185 static nxt_int_t 186 nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules, 187 const char *name) 188 { 189 void *dl; 190 nxt_str_t version; 191 nxt_int_t ret; 192 nxt_uint_t i, n; 193 nxt_module_t *module; 194 nxt_app_type_t type; 195 nxt_application_module_t *app; 196 197 /* 198 * Only memory allocation failure should return NXT_ERROR. 199 * Any module processing errors are ignored. 200 */ 201 ret = NXT_ERROR; 202 203 dl = dlopen(name, RTLD_GLOBAL | RTLD_NOW); 204 205 if (dl == NULL) { 206 nxt_alert(task, "dlopen(\"%s\"), failed: \"%s\"", name, dlerror()); 207 return NXT_OK; 208 } 209 210 app = dlsym(dl, "nxt_app_module"); 211 212 if (app != NULL) { 213 nxt_log(task, NXT_LOG_NOTICE, "module: %V %s \"%s\"", 214 &app->type, app->version, name); 215 216 if (app->compat_length != sizeof(compat) 217 || nxt_memcmp(app->compat, compat, sizeof(compat)) != 0) 218 { 219 nxt_log(task, NXT_LOG_NOTICE, "incompatible module %s", name); 220 221 goto done; 222 } 223 224 type = nxt_app_parse_type(app->type.start, app->type.length); 225 226 if (type == NXT_APP_UNKNOWN) { 227 nxt_log(task, NXT_LOG_NOTICE, "unknown module type %V", &app->type); 228 229 goto done; 230 } 231 232 module = modules->elts; 233 n = modules->nelts; 234 235 version.start = (u_char *) app->version; 236 version.length = nxt_strlen(app->version); 237 238 for (i = 0; i < n; i++) { 239 if (type == module[i].type 240 && nxt_strstr_eq(&module[i].version, &version)) 241 { 242 nxt_log(task, NXT_LOG_NOTICE, 243 "ignoring %s module with the same " 244 "application language version %V %V as in %V", 245 name, &app->type, &version, &module[i].file); 246 247 goto done; 248 } 249 } 250 251 module = nxt_array_add(modules); 252 if (module == NULL) { 253 goto fail; 254 } 255 256 module->type = type; 257 258 nxt_str_dup(mp, &module->version, &version); 259 if (module->version.start == NULL) { 260 goto fail; 261 } 262 263 module->file.length = nxt_strlen(name); 264 265 module->file.start = nxt_mp_alloc(mp, module->file.length); 266 if (module->file.start == NULL) { 267 goto fail; 268 } 269 270 nxt_memcpy(module->file.start, name, module->file.length); 271 272 } else { 273 nxt_alert(task, "dlsym(\"%s\"), failed: \"%s\"", name, dlerror()); 274 } 275 276 done: 277 278 ret = NXT_OK; 279 280 fail: 281 282 if (dlclose(dl) != 0) { 283 nxt_alert(task, "dlclose(\"%s\"), failed: \"%s\"", name, dlerror()); 284 } 285 286 return ret; 287 } 288 289 290 static void 291 nxt_discovery_completion_handler(nxt_task_t *task, void *obj, void *data) 292 { 293 nxt_mp_t *mp; 294 nxt_buf_t *b; 295 296 b = obj; 297 mp = b->data; 298 299 nxt_mp_destroy(mp); 300 } 301 302 303 static void 304 nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) 305 { 306 nxt_worker_process_quit_handler(task, msg); 307 } 308 309 310 nxt_int_t 311 nxt_app_start(nxt_task_t *task, void *data) 312 { 313 nxt_int_t ret; 314 nxt_app_lang_module_t *lang; 315 nxt_common_app_conf_t *app_conf; 316 317 app_conf = data; 318 319 lang = nxt_app_lang_module(task->thread->runtime, &app_conf->type); 320 if (nxt_slow_path(lang == NULL)) { 321 nxt_alert(task, "unknown application type: \"%V\"", &app_conf->type); 322 return NXT_ERROR; 323 } 324 325 nxt_app = lang->module; 326 327 if (nxt_app == NULL) { 328 nxt_debug(task, "application language module: %s \"%s\"", 329 lang->version, lang->file); 330 331 nxt_app = nxt_app_module_load(task, lang->file); 332 } 333 334 if (app_conf->working_directory != NULL 335 && app_conf->working_directory[0] != 0) 336 { 337 ret = chdir(app_conf->working_directory); 338 339 if (nxt_slow_path(ret != 0)) { 340 nxt_log(task, NXT_LOG_WARN, "chdir(%s) failed %E", 341 app_conf->working_directory, nxt_errno); 342 343 return NXT_ERROR; 344 } 345 } 346 347 if (nxt_slow_path(nxt_thread_mutex_create(&nxt_app_mutex) != NXT_OK)) { 348 return NXT_ERROR; 349 } 350 351 if (nxt_slow_path(nxt_thread_cond_create(&nxt_app_cond) != NXT_OK)) { 352 return NXT_ERROR; 353 } 354 355 ret = nxt_app->init(task, data); 356 357 if (nxt_slow_path(ret != NXT_OK)) { 358 nxt_debug(task, "application init failed"); 359 360 } else { 361 nxt_debug(task, "application init done"); 362 } 363 364 return ret; 365 } 366 367 368 static nxt_app_module_t * 369 nxt_app_module_load(nxt_task_t *task, const char *name) 370 { 371 void *dl; 372 373 dl = dlopen(name, RTLD_GLOBAL | RTLD_LAZY); 374 375 if (dl != NULL) { 376 return dlsym(dl, "nxt_app_module"); 377 } 378 379 nxt_alert(task, "dlopen(\"%s\"), failed: \"%s\"", name, dlerror()); 380 381 return NULL; 382 } 383 384 385 void 386 nxt_app_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) 387 { 388 if (nxt_app->atexit != NULL) { 389 nxt_app->atexit(task); 390 } 391 392 nxt_worker_process_quit_handler(task, msg); 393 } 394 395 396 void 397 nxt_app_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) 398 { 399 size_t dump_size; 400 nxt_int_t res; 401 nxt_buf_t *b; 402 nxt_port_t *port; 403 nxt_app_rmsg_t rmsg = { msg->buf }; 404 nxt_app_wmsg_t wmsg; 405 406 b = msg->buf; 407 dump_size = b->mem.free - b->mem.pos; 408 409 if (dump_size > 300) { 410 dump_size = 300; 411 } 412 413 nxt_debug(task, "app data: %*s ...", dump_size, b->mem.pos); 414 415 port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid, 416 msg->port_msg.reply_port); 417 if (nxt_slow_path(port == NULL)) { 418 nxt_debug(task, "stream #%uD: reply port %d not found", 419 msg->port_msg.stream, msg->port_msg.reply_port); 420 return; 421 } 422 423 wmsg.port = port; 424 wmsg.write = NULL; 425 wmsg.buf = &wmsg.write; 426 wmsg.stream = msg->port_msg.stream; 427 428 res = nxt_app->run(task, &rmsg, &wmsg); 429 430 if (nxt_slow_path(res != NXT_OK)) { 431 nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR, -1, 432 msg->port_msg.stream, 0, NULL); 433 } 434 } 435 436 437 u_char * 438 nxt_app_msg_write_get_buf(nxt_task_t *task, nxt_app_wmsg_t *msg, size_t size) 439 { 440 size_t free_size; 441 u_char *res; 442 nxt_buf_t *b; 443 444 res = NULL; 445 446 do { 447 b = *msg->buf; 448 449 if (b == NULL) { 450 b = nxt_port_mmap_get_buf(task, msg->port, size); 451 if (nxt_slow_path(b == NULL)) { 452 return NULL; 453 } 454 455 *msg->buf = b; 456 457 free_size = nxt_buf_mem_free_size(&b->mem); 458 459 if (nxt_slow_path(free_size < size)) { 460 nxt_log(task, NXT_LOG_WARN, "requested buffer too big " 461 "(%z < %z)", free_size, size); 462 return NULL; 463 } 464 465 } 466 467 free_size = nxt_buf_mem_free_size(&b->mem); 468 469 if (free_size >= size) { 470 res = b->mem.free; 471 b->mem.free += size; 472 473 return res; 474 } 475 476 if (nxt_port_mmap_increase_buf(task, b, size, size) == NXT_OK) { 477 res = b->mem.free; 478 b->mem.free += size; 479 480 return res; 481 } 482 483 msg->buf = &b->next; 484 } while(1); 485 } 486 487 488 nxt_int_t 489 nxt_app_msg_write(nxt_task_t *task, nxt_app_wmsg_t *msg, u_char *c, size_t size) 490 { 491 u_char *dst; 492 size_t dst_length; 493 494 if (c != NULL) { 495 dst_length = size + (size < 128 ? 1 : 4) + 1; 496 497 dst = nxt_app_msg_write_get_buf(task, msg, dst_length); 498 if (nxt_slow_path(dst == NULL)) { 499 nxt_debug(task, "nxt_app_msg_write: get_buf(%uz) failed", 500 dst_length); 501 return NXT_ERROR; 502 } 503 504 dst = nxt_app_msg_write_length(dst, size + 1); /* +1 for trailing 0 */ 505 506 nxt_memcpy(dst, c, size); 507 dst[size] = 0; 508 509 nxt_debug(task, "nxt_app_msg_write: %uz %*s", size, size, c); 510 511 } else { 512 dst_length = 1; 513 514 dst = nxt_app_msg_write_get_buf(task, msg, dst_length); 515 if (nxt_slow_path(dst == NULL)) { 516 nxt_debug(task, "nxt_app_msg_write: get_buf(%uz) failed", 517 dst_length); 518 return NXT_ERROR; 519 } 520 521 dst = nxt_app_msg_write_length(dst, 0); 522 523 nxt_debug(task, "nxt_app_msg_write: NULL"); 524 } 525 526 return NXT_OK; 527 } 528 529 530 nxt_int_t 531 nxt_app_msg_write_prefixed_upcase(nxt_task_t *task, nxt_app_wmsg_t *msg, 532 const nxt_str_t *prefix, u_char *c, size_t size) 533 { 534 u_char *dst, *src; 535 size_t i, length, dst_length; 536 537 length = prefix->length + size; 538 539 dst_length = length + (length < 128 ? 1 : 4) + 1; 540 541 dst = nxt_app_msg_write_get_buf(task, msg, dst_length); 542 if (nxt_slow_path(dst == NULL)) { 543 return NXT_ERROR; 544 } 545 546 dst = nxt_app_msg_write_length(dst, length + 1); /* +1 for trailing 0 */ 547 548 nxt_memcpy(dst, prefix->start, prefix->length); 549 dst += prefix->length; 550 551 src = c; 552 for (i = 0; i < size; i++, dst++, src++) { 553 554 if (*src >= 'a' && *src <= 'z') { 555 *dst = *src & ~0x20; 556 continue; 557 } 558 559 if (*src == '-') { 560 *dst = '_'; 561 continue; 562 } 563 564 *dst = *src; 565 } 566 567 *dst = 0; 568 569 return NXT_OK; 570 } 571 572 573 nxt_inline nxt_int_t 574 nxt_app_msg_read_size_(nxt_task_t *task, nxt_app_rmsg_t *msg, size_t *size) 575 { 576 nxt_buf_t *buf; 577 578 do { 579 buf = msg->buf; 580 581 if (nxt_slow_path(buf == NULL)) { 582 return NXT_DONE; 583 } 584 585 if (nxt_slow_path(nxt_buf_mem_used_size(&buf->mem) < 1)) { 586 if (nxt_fast_path(nxt_buf_mem_used_size(&buf->mem) == 0)) { 587 msg->buf = buf->next; 588 continue; 589 } 590 return NXT_ERROR; 591 } 592 593 if (buf->mem.pos[0] >= 128) { 594 if (nxt_slow_path(nxt_buf_mem_used_size(&buf->mem) < 4)) { 595 return NXT_ERROR; 596 } 597 } 598 599 break; 600 } while (1); 601 602 buf->mem.pos = nxt_app_msg_read_length(buf->mem.pos, size); 603 604 return NXT_OK; 605 } 606 607 608 nxt_int_t 609 nxt_app_msg_read_str(nxt_task_t *task, nxt_app_rmsg_t *msg, nxt_str_t *str) 610 { 611 size_t length; 612 nxt_int_t ret; 613 nxt_buf_t *buf; 614 615 ret = nxt_app_msg_read_size_(task, msg, &length); 616 if (ret != NXT_OK) { 617 return ret; 618 } 619 620 buf = msg->buf; 621 622 if (nxt_slow_path(nxt_buf_mem_used_size(&buf->mem) < (intptr_t) length)) { 623 return NXT_ERROR; 624 } 625 626 if (length > 0) { 627 str->start = buf->mem.pos; 628 str->length = length - 1; 629 630 buf->mem.pos += length; 631 632 nxt_debug(task, "nxt_read_str: %uz %*s", length - 1, 633 length - 1, str->start); 634 635 } else { 636 str->start = NULL; 637 str->length = 0; 638 639 nxt_debug(task, "nxt_read_str: NULL"); 640 } 641 642 return NXT_OK; 643 } 644 645 646 size_t 647 nxt_app_msg_read_raw(nxt_task_t *task, nxt_app_rmsg_t *msg, void *dst, 648 size_t size) 649 { 650 size_t res, read_size; 651 nxt_buf_t *buf; 652 653 res = 0; 654 655 while (size > 0) { 656 buf = msg->buf; 657 658 if (nxt_slow_path(buf == NULL)) { 659 break; 660 } 661 662 if (nxt_slow_path(nxt_buf_mem_used_size(&buf->mem) == 0)) { 663 msg->buf = buf->next; 664 continue; 665 } 666 667 read_size = nxt_buf_mem_used_size(&buf->mem); 668 read_size = nxt_min(read_size, size); 669 670 dst = nxt_cpymem(dst, buf->mem.pos, read_size); 671 672 size -= read_size; 673 buf->mem.pos += read_size; 674 res += read_size; 675 } 676 677 nxt_debug(task, "nxt_read_raw: %uz", res); 678 679 return res; 680 } 681 682 683 nxt_int_t 684 nxt_app_msg_read_nvp(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_str_t *n, 685 nxt_str_t *v) 686 { 687 nxt_int_t rc; 688 689 rc = nxt_app_msg_read_str(task, rmsg, n); 690 if (nxt_slow_path(rc != NXT_OK)) { 691 return rc; 692 } 693 694 rc = nxt_app_msg_read_str(task, rmsg, v); 695 if (nxt_slow_path(rc != NXT_OK)) { 696 return rc; 697 } 698 699 return rc; 700 } 701 702 703 nxt_int_t 704 nxt_app_msg_read_size(nxt_task_t *task, nxt_app_rmsg_t *msg, size_t *size) 705 { 706 nxt_int_t ret; 707 708 ret = nxt_app_msg_read_size_(task, msg, size); 709 710 nxt_debug(task, "nxt_read_size: %d", (int) *size); 711 712 return ret; 713 } 714 715 716 nxt_int_t 717 nxt_app_http_req_done(nxt_task_t *task, nxt_app_parse_ctx_t *ar) 718 { 719 ar->timer.handler = nxt_app_http_release; 720 nxt_timer_add(task->thread->engine, &ar->timer, 0); 721 722 return NXT_OK; 723 } 724 725 726 static void 727 nxt_app_http_release(nxt_task_t *task, void *obj, void *data) 728 { 729 nxt_timer_t *timer; 730 nxt_app_parse_ctx_t *ar; 731 732 timer = obj; 733 734 nxt_debug(task, "http app release"); 735 736 ar = nxt_timer_data(timer, nxt_app_parse_ctx_t, timer); 737 738 nxt_mp_release(ar->request->mem_pool); 739 } 740 741 742 nxt_int_t 743 nxt_app_msg_flush(nxt_task_t *task, nxt_app_wmsg_t *msg, nxt_bool_t last) 744 { 745 nxt_int_t rc; 746 nxt_buf_t *b; 747 748 rc = NXT_OK; 749 750 if (nxt_slow_path(last == 1)) { 751 do { 752 b = *msg->buf; 753 754 if (b == NULL) { 755 b = nxt_buf_sync_alloc(msg->port->mem_pool, NXT_BUF_SYNC_LAST); 756 *msg->buf = b; 757 break; 758 } 759 760 msg->buf = &b->next; 761 } while(1); 762 } 763 764 if (nxt_slow_path(msg->write != NULL)) { 765 rc = nxt_port_socket_write(task, msg->port, NXT_PORT_MSG_DATA, 766 -1, msg->stream, 0, msg->write); 767 768 msg->write = NULL; 769 msg->buf = &msg->write; 770 } 771 772 return rc; 773 } 774 775 776 nxt_int_t 777 nxt_app_msg_write_raw(nxt_task_t *task, nxt_app_wmsg_t *msg, const u_char *c, 778 size_t size) 779 { 780 size_t free_size, copy_size; 781 nxt_buf_t *b; 782 783 nxt_debug(task, "nxt_app_msg_write_raw: %uz", size); 784 785 while (size > 0) { 786 b = *msg->buf; 787 788 if (b == NULL) { 789 b = nxt_port_mmap_get_buf(task, msg->port, size); 790 if (nxt_slow_path(b == NULL)) { 791 return NXT_ERROR; 792 } 793 794 *msg->buf = b; 795 } 796 797 do { 798 free_size = nxt_buf_mem_free_size(&b->mem); 799 800 if (free_size > 0) { 801 copy_size = nxt_min(free_size, size); 802 803 b->mem.free = nxt_cpymem(b->mem.free, c, copy_size); 804 805 size -= copy_size; 806 c += copy_size; 807 808 if (size == 0) { 809 return NXT_OK; 810 } 811 } 812 } while (nxt_port_mmap_increase_buf(task, b, size, 1) == NXT_OK); 813 814 msg->buf = &b->next; 815 } 816 817 return NXT_OK; 818 } 819 820 821 nxt_app_lang_module_t * 822 nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name) 823 { 824 u_char *p, *end, *version; 825 size_t version_length; 826 nxt_uint_t i, n; 827 nxt_app_type_t type; 828 nxt_app_lang_module_t *lang; 829 830 end = name->start + name->length; 831 version = end; 832 833 for (p = name->start; p < end; p++) { 834 if (*p == ' ') { 835 version = p + 1; 836 break; 837 } 838 839 if (*p >= '0' && *p <= '9') { 840 version = p; 841 break; 842 } 843 } 844 845 type = nxt_app_parse_type(name->start, p - name->start); 846 847 if (type == NXT_APP_UNKNOWN) { 848 return NULL; 849 } 850 851 version_length = end - version; 852 853 lang = rt->languages->elts; 854 n = rt->languages->nelts; 855 856 for (i = 0; i < n; i++) { 857 858 /* 859 * Versions are sorted in descending order 860 * so first match chooses the highest version. 861 */ 862 863 if (lang[i].type == type 864 && nxt_strvers_match(lang[i].version, version, version_length)) 865 { 866 return &lang[i]; 867 } 868 } 869 870 return NULL; 871 } 872 873 874 nxt_app_type_t 875 nxt_app_parse_type(u_char *p, size_t length) 876 { 877 nxt_str_t str; 878 879 str.length = length; 880 str.start = p; 881 882 if (nxt_str_eq(&str, "python", 6)) { 883 return NXT_APP_PYTHON; 884 885 } else if (nxt_str_eq(&str, "php", 3)) { 886 return NXT_APP_PHP; 887 888 } else if (nxt_str_eq(&str, "go", 2)) { 889 return NXT_APP_GO; 890 891 } else if (nxt_str_eq(&str, "perl", 4)) { 892 return NXT_APP_PERL; 893 894 } else if (nxt_str_eq(&str, "ruby", 4)) { 895 return NXT_APP_RUBY; 896 } 897 898 return NXT_APP_UNKNOWN; 899 } 900