1 2 /* 3 * Copyright (C) NGINX, Inc. 4 */ 5 6 #include "unit.h" 7 8 9 napi_ref Unit::constructor_; 10 11 12 Unit::Unit(napi_env env): 13 env_(env), 14 wrapper_(nullptr), 15 unit_ctx_(nullptr) 16 { 17 } 18 19 20 Unit::~Unit() 21 { 22 napi_delete_reference(env_, wrapper_); 23 } 24 25 26 napi_value 27 Unit::init(napi_env env, napi_value exports) 28 { 29 napi_value cons, fn; 30 napi_status status; 31 32 napi_property_descriptor properties[] = { 33 { "createServer", 0, create_server, 0, 0, 0, napi_default, 0 }, 34 { "listen", 0, listen, 0, 0, 0, napi_default, 0 } 35 }; 36 37 status = napi_define_class(env, "Unit", NAPI_AUTO_LENGTH, create, nullptr, 38 2, properties, &cons); 39 if (status != napi_ok) { 40 goto failed; 41 } 42 43 status = napi_create_reference(env, cons, 1, &constructor_); 44 if (status != napi_ok) { 45 goto failed; 46 } 47 48 status = napi_set_named_property(env, exports, "Unit", cons); 49 if (status != napi_ok) { 50 goto failed; 51 } 52 53 status = napi_create_function(env, NULL, 0, response_send_headers, NULL, 54 &fn); 55 if (status != napi_ok) { 56 goto failed; 57 } 58 59 status = napi_set_named_property(env, exports, 60 "unit_response_headers", fn); 61 if (status != napi_ok) { 62 goto failed; 63 } 64 65 status = napi_create_function(env, NULL, 0, response_write, NULL, &fn); 66 if (status != napi_ok) { 67 goto failed; 68 } 69 70 status = napi_set_named_property(env, exports, "unit_response_write", fn); 71 if (status != napi_ok) { 72 goto failed; 73 } 74 75 status = napi_create_function(env, NULL, 0, response_end, NULL, &fn); 76 if (status != napi_ok) { 77 goto failed; 78 } 79 80 status = napi_set_named_property(env, exports, "unit_response_end", fn); 81 if (status != napi_ok) { 82 goto failed; 83 } 84 85 return exports; 86 87 failed: 88 89 napi_throw_error(env, NULL, "Failed to define Unit class"); 90 91 return nullptr; 92 } 93 94 95 void 96 Unit::destroy(napi_env env, void *nativeObject, void *finalize_hint) 97 { 98 Unit *obj = reinterpret_cast<Unit *>(nativeObject); 99 100 delete obj; 101 } 102 103 104 napi_value 105 Unit::create(napi_env env, napi_callback_info info) 106 { 107 Unit *obj; 108 napi_value target, cons, instance, jsthis; 109 napi_status status; 110 111 status = napi_get_new_target(env, info, &target); 112 if (status != napi_ok) { 113 goto failed; 114 } 115 116 if (target != nullptr) { 117 /* Invoked as constructor: `new Unit(...)` */ 118 status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, 119 nullptr); 120 if (status != napi_ok) { 121 goto failed; 122 } 123 124 obj = new Unit(env); 125 126 status = napi_wrap(env, jsthis, reinterpret_cast<void *>(obj), 127 destroy, nullptr, &obj->wrapper_); 128 if (status != napi_ok) { 129 goto failed; 130 } 131 132 return jsthis; 133 } 134 135 /* Invoked as plain function `Unit(...)`, turn into construct call. */ 136 status = napi_get_reference_value(env, constructor_, &cons); 137 if (status != napi_ok) { 138 goto failed; 139 } 140 141 status = napi_new_instance(env, cons, 0, nullptr, &instance); 142 if (status != napi_ok) { 143 goto failed; 144 } 145 146 return instance; 147 148 failed: 149 150 napi_throw_error(env, NULL, "Failed to create Unit object"); 151 152 return nullptr; 153 } 154 155 156 napi_value 157 Unit::create_server(napi_env env, napi_callback_info info) 158 { 159 Unit *obj; 160 size_t argc; 161 napi_value jsthis; 162 napi_status status; 163 napi_value argv[1]; 164 nxt_unit_init_t unit_init; 165 166 argc = 1; 167 168 status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr); 169 if (status != napi_ok) { 170 goto failed; 171 } 172 173 status = napi_unwrap(env, jsthis, reinterpret_cast<void **>(&obj)); 174 if (status != napi_ok) { 175 goto failed; 176 } 177 178 memset(&unit_init, 0, sizeof(nxt_unit_init_t)); 179 180 unit_init.data = obj; 181 unit_init.callbacks.request_handler = request_handler; 182 183 obj->unit_ctx_ = nxt_unit_init(&unit_init); 184 if (obj->unit_ctx_ == NULL) { 185 goto failed; 186 } 187 188 return nullptr; 189 190 failed: 191 192 napi_throw_error(env, NULL, "Failed to create Unit object"); 193 194 return nullptr; 195 } 196 197 198 napi_value 199 Unit::listen(napi_env env, napi_callback_info info) 200 { 201 int ret; 202 Unit *obj; 203 napi_value jsthis; 204 napi_status status; 205 206 status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr); 207 if (status != napi_ok) { 208 goto failed; 209 } 210 211 status = napi_unwrap(env, jsthis, reinterpret_cast<void **>(&obj)); 212 if (status != napi_ok) { 213 goto failed; 214 } 215 216 if (obj->unit_ctx_ == NULL) { 217 napi_throw_error(env, NULL, "Unit context was not created"); 218 return nullptr; 219 } 220 221 ret = nxt_unit_run(obj->unit_ctx_); 222 if (ret != NXT_UNIT_OK) { 223 napi_throw_error(env, NULL, "Failed to run Unit"); 224 return nullptr; 225 } 226 227 nxt_unit_done(obj->unit_ctx_); 228 229 return nullptr; 230 231 failed: 232 233 napi_throw_error(env, NULL, "Failed to listen Unit socket"); 234 235 return nullptr; 236 } 237 238 239 void 240 Unit::request_handler(nxt_unit_request_info_t *req) 241 { 242 Unit *obj; 243 napi_value socket, request, response; 244 napi_value global, server_obj; 245 napi_value req_argv[3]; 246 napi_status status; 247 248 obj = reinterpret_cast<Unit *>(req->unit->data); 249 250 napi_handle_scope scope; 251 status = napi_open_handle_scope(obj->env_, &scope); 252 if (status != napi_ok) { 253 napi_throw_error(obj->env_, NULL, "Failed to create handle scope"); 254 return; 255 } 256 257 server_obj = obj->get_server_object(); 258 if (server_obj == nullptr) { 259 napi_throw_error(obj->env_, NULL, "Failed to get server object"); 260 return; 261 } 262 263 status = napi_get_global(obj->env_, &global); 264 if (status != napi_ok) { 265 napi_throw_error(obj->env_, NULL, "Failed to get global variable"); 266 return; 267 } 268 269 socket = obj->create_socket(server_obj, req); 270 if (socket == nullptr) { 271 napi_throw_error(obj->env_, NULL, "Failed to create socket object"); 272 return; 273 } 274 275 request = obj->create_request(server_obj, socket); 276 if (request == nullptr) { 277 napi_throw_error(obj->env_, NULL, "Failed to create request object"); 278 return; 279 } 280 281 response = obj->create_response(server_obj, socket, request, req, obj); 282 if (response == nullptr) { 283 napi_throw_error(obj->env_, NULL, "Failed to create response object"); 284 return; 285 } 286 287 req_argv[1] = request; 288 req_argv[2] = response; 289 290 status = obj->create_headers(req, request); 291 if (status != napi_ok) { 292 napi_throw_error(obj->env_, NULL, "Failed to create headers"); 293 return; 294 } 295 296 obj->emit(server_obj, "request", sizeof("request") - 1, 3, req_argv); 297 obj->emit_post_data(request, req); 298 299 napi_close_handle_scope(obj->env_, scope); 300 } 301 302 303 napi_value 304 Unit::get_server_object() 305 { 306 napi_value unit_obj, server_obj; 307 napi_status status; 308 309 status = napi_get_reference_value(env_, wrapper_, &unit_obj); 310 if (status != napi_ok) { 311 return nullptr; 312 } 313 314 status = napi_get_named_property(env_, unit_obj, "server", &server_obj); 315 if (status != napi_ok) { 316 return nullptr; 317 } 318 319 return server_obj; 320 } 321 322 323 napi_value 324 Unit::emit(napi_value obj, const char *name, size_t name_len, size_t argc, 325 napi_value *argv) 326 { 327 napi_value emitter, return_val, str; 328 napi_status status; 329 330 status = napi_get_named_property(env_, obj, "emit", &emitter); 331 if (status != napi_ok) { 332 return nullptr; 333 } 334 335 status = napi_create_string_latin1(env_, name, name_len, &str); 336 if (status != napi_ok) { 337 return nullptr; 338 } 339 340 if (argc != 0) { 341 argv[0] = str; 342 343 } else { 344 argc = 1; 345 argv = &str; 346 } 347 348 status = napi_call_function(env_, obj, emitter, argc, argv, &return_val); 349 if (status != napi_ok) { 350 return nullptr; 351 } 352 353 return return_val; 354 } 355 356 357 napi_status 358 Unit::create_headers(nxt_unit_request_info_t *req, napi_value request) 359 { 360 uint32_t i; 361 const char *p; 362 napi_value headers, raw_headers, str; 363 napi_status status; 364 nxt_unit_field_t *f; 365 nxt_unit_request_t *r; 366 367 r = req->request; 368 369 status = napi_create_object(env_, &headers); 370 if (status != napi_ok) { 371 return status; 372 } 373 374 status = napi_create_array_with_length(env_, r->fields_count * 2, 375 &raw_headers); 376 if (status != napi_ok) { 377 return status; 378 } 379 380 for (i = 0; i < r->fields_count; i++) { 381 f = r->fields + i; 382 383 status = this->append_header(f, headers, raw_headers, i); 384 if (status != napi_ok) { 385 return status; 386 } 387 } 388 389 status = napi_set_named_property(env_, request, "headers", headers); 390 if (status != napi_ok) { 391 return status; 392 } 393 394 status = napi_set_named_property(env_, request, "raw_headers", raw_headers); 395 if (status != napi_ok) { 396 return status; 397 } 398 399 p = (const char *) nxt_unit_sptr_get(&r->version); 400 401 status = napi_create_string_latin1(env_, p, r->version_length, &str); 402 if (status != napi_ok) { 403 return status; 404 } 405 406 status = napi_set_named_property(env_, request, "httpVersion", str); 407 if (status != napi_ok) { 408 return status; 409 } 410 411 p = (const char *) nxt_unit_sptr_get(&r->method); 412 413 status = napi_create_string_latin1(env_, p, r->method_length, &str); 414 if (status != napi_ok) { 415 return status; 416 } 417 418 status = napi_set_named_property(env_, request, "method", str); 419 if (status != napi_ok) { 420 return status; 421 } 422 423 p = (const char *) nxt_unit_sptr_get(&r->target); 424 425 status = napi_create_string_latin1(env_, p, r->target_length, &str); 426 if (status != napi_ok) { 427 return status; 428 } 429 430 status = napi_set_named_property(env_, request, "url", str); 431 if (status != napi_ok) { 432 return status; 433 } 434 435 return napi_ok; 436 } 437 438 439 inline napi_status 440 Unit::append_header(nxt_unit_field_t *f, napi_value headers, 441 napi_value raw_headers, uint32_t idx) 442 { 443 const char *name, *value; 444 napi_value str, vstr; 445 napi_status status; 446 447 value = (const char *) nxt_unit_sptr_get(&f->value); 448 449 status = napi_create_string_latin1(env_, value, f->value_length, &vstr); 450 if (status != napi_ok) { 451 return status; 452 } 453 454 name = (const char *) nxt_unit_sptr_get(&f->name); 455 456 status = napi_set_named_property(env_, headers, name, vstr); 457 if (status != napi_ok) { 458 return status; 459 } 460 461 status = napi_create_string_latin1(env_, name, f->name_length, &str); 462 if (status != napi_ok) { 463 return status; 464 } 465 466 status = napi_set_element(env_, raw_headers, idx * 2, str); 467 if (status != napi_ok) { 468 return status; 469 } 470 471 status = napi_set_element(env_, raw_headers, idx * 2 + 1, vstr); 472 if (status != napi_ok) { 473 return status; 474 } 475 476 return napi_ok; 477 } 478 479 480 napi_value 481 Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req) 482 { 483 napi_value constructor, return_val; 484 napi_status status; 485 486 status = napi_get_named_property(env_, server_obj, "socket", 487 &constructor); 488 if (status != napi_ok) { 489 return nullptr; 490 } 491 492 status = napi_new_instance(env_, constructor, 0, NULL, &return_val); 493 if (status != napi_ok) { 494 return nullptr; 495 } 496 497 return return_val; 498 } 499 500 501 napi_value 502 Unit::create_request(napi_value server_obj, napi_value socket) 503 { 504 napi_value constructor, return_val; 505 napi_status status; 506 507 status = napi_get_named_property(env_, server_obj, "request", 508 &constructor); 509 if (status != napi_ok) { 510 return nullptr; 511 } 512 513 status = napi_new_instance(env_, constructor, 1, &server_obj, 514 &return_val); 515 if (status != napi_ok) { 516 return nullptr; 517 } 518 519 status = napi_set_named_property(env_, return_val, "socket", socket); 520 if (status != napi_ok) { 521 return nullptr; 522 } 523 524 return return_val; 525 } 526 527 528 napi_value 529 Unit::create_response(napi_value server_obj, napi_value socket, 530 napi_value request, nxt_unit_request_info_t *req, 531 Unit *obj) 532 { 533 napi_value constructor, return_val, req_num; 534 napi_status status; 535 536 status = napi_get_named_property(env_, server_obj, "response", 537 &constructor); 538 if (status != napi_ok) { 539 return nullptr; 540 } 541 542 status = napi_new_instance(env_, constructor, 1, &request, &return_val); 543 if (status != napi_ok) { 544 return nullptr; 545 } 546 547 status = napi_set_named_property(env_, return_val, "socket", socket); 548 if (status != napi_ok) { 549 return nullptr; 550 } 551 552 status = napi_create_int64(env_, (int64_t) (uintptr_t) req, &req_num); 553 if (status != napi_ok) { 554 return nullptr; 555 } 556 557 status = napi_set_named_property(env_, return_val, "_req_point", req_num); 558 if (status != napi_ok) { 559 return nullptr; 560 } 561 562 return return_val; 563 } 564 565 566 void 567 Unit::emit_post_data(napi_value request, nxt_unit_request_info_t *req) 568 { 569 void *data; 570 napi_value req_argv[2]; 571 napi_status status; 572 573 status = napi_create_buffer(env_, (size_t) req->content_length, 574 &data, &req_argv[1]); 575 if (status != napi_ok) { 576 napi_throw_error(env_, NULL, "Failed to create request buffer"); 577 return; 578 } 579 580 nxt_unit_request_read(req, data, req->content_length); 581 582 emit(request, "data", sizeof("data") - 1, 2, req_argv); 583 emit(request, "end", sizeof("end") - 1, 0, nullptr); 584 } 585 586 587 napi_value 588 Unit::response_send_headers(napi_env env, napi_callback_info info) 589 { 590 int ret; 591 char *ptr, *name_ptr; 592 bool is_array; 593 size_t argc, name_len, value_len; 594 int64_t req_p; 595 uint32_t status_code, header_len, keys_len, array_len; 596 uint32_t keys_count, i, j; 597 uint16_t hash; 598 napi_value this_arg, headers, keys, name, value, array_val; 599 napi_value req_num; 600 napi_status status; 601 nxt_unit_field_t *f; 602 nxt_unit_request_info_t *req; 603 napi_value argv[5]; 604 605 argc = 5; 606 607 status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL); 608 if (status != napi_ok) { 609 return nullptr; 610 } 611 612 if (argc != 5) { 613 napi_throw_error(env, NULL, "Wrong args count. Need three: " 614 "statusCode, headers, headers count, headers length"); 615 return nullptr; 616 } 617 618 status = napi_get_named_property(env, argv[0], "_req_point", &req_num); 619 if (status != napi_ok) { 620 napi_throw_error(env, NULL, "Failed to get request pointer"); 621 return nullptr; 622 } 623 624 status = napi_get_value_int64(env, req_num, &req_p); 625 if (status != napi_ok) { 626 napi_throw_error(env, NULL, "Failed to get request pointer"); 627 return nullptr; 628 } 629 630 req = (nxt_unit_request_info_t *) (uintptr_t) req_p; 631 632 status = napi_get_value_uint32(env, argv[1], &status_code); 633 if (status != napi_ok) { 634 goto failed; 635 } 636 637 status = napi_get_value_uint32(env, argv[3], &keys_count); 638 if (status != napi_ok) { 639 goto failed; 640 } 641 642 status = napi_get_value_uint32(env, argv[4], &header_len); 643 if (status != napi_ok) { 644 goto failed; 645 } 646 647 /* Need to reserve extra byte for C-string 0-termination. */ 648 header_len++; 649 650 headers = argv[2]; 651 652 ret = nxt_unit_response_init(req, status_code, keys_count, header_len); 653 if (ret != NXT_UNIT_OK) { 654 goto failed; 655 } 656 657 status = napi_get_property_names(env, headers, &keys); 658 if (status != napi_ok) { 659 goto failed; 660 } 661 662 status = napi_get_array_length(env, keys, &keys_len); 663 if (status != napi_ok) { 664 goto failed; 665 } 666 667 ptr = req->response_buf->free; 668 669 for (i = 0; i < keys_len; i++) { 670 status = napi_get_element(env, keys, i, &name); 671 if (status != napi_ok) { 672 goto failed; 673 } 674 675 status = napi_get_property(env, headers, name, &value); 676 if (status != napi_ok) { 677 goto failed; 678 } 679 680 status = napi_get_value_string_latin1(env, name, ptr, header_len, 681 &name_len); 682 if (status != napi_ok) { 683 goto failed; 684 } 685 686 name_ptr = ptr; 687 688 ptr += name_len; 689 header_len -= name_len; 690 691 hash = nxt_unit_field_hash(name_ptr, name_len); 692 693 status = napi_is_array(env, value, &is_array); 694 if (status != napi_ok) { 695 goto failed; 696 } 697 698 if (is_array) { 699 status = napi_get_array_length(env, value, &array_len); 700 if (status != napi_ok) { 701 goto failed; 702 } 703 704 for (j = 0; j < array_len; j++) { 705 status = napi_get_element(env, value, j, &array_val); 706 if (status != napi_ok) { 707 goto failed; 708 } 709 710 status = napi_get_value_string_latin1(env, array_val, ptr, 711 header_len, 712 &value_len); 713 if (status != napi_ok) { 714 goto failed; 715 } 716 717 f = req->response->fields + req->response->fields_count; 718 f->skip = 0; 719 720 nxt_unit_sptr_set(&f->name, name_ptr); 721 722 f->name_length = name_len; 723 f->hash = hash; 724 725 nxt_unit_sptr_set(&f->value, ptr); 726 f->value_length = (uint32_t) value_len; 727 728 ptr += value_len; 729 header_len -= value_len; 730 731 req->response->fields_count++; 732 } 733 734 } else { 735 status = napi_get_value_string_latin1(env, value, ptr, header_len, 736 &value_len); 737 if (status != napi_ok) { 738 goto failed; 739 } 740 741 f = req->response->fields + req->response->fields_count; 742 f->skip = 0; 743 744 nxt_unit_sptr_set(&f->name, name_ptr); 745 746 f->name_length = name_len; 747 f->hash = hash; 748 749 nxt_unit_sptr_set(&f->value, ptr); 750 f->value_length = (uint32_t) value_len; 751 752 ptr += value_len; 753 header_len -= value_len; 754 755 req->response->fields_count++; 756 } 757 } 758 759 req->response_buf->free = ptr; 760 761 ret = nxt_unit_response_send(req); 762 if (ret != NXT_UNIT_OK) { 763 goto failed; 764 } 765 766 return this_arg; 767 768 failed: 769 770 req->response->fields_count = 0; 771 772 napi_throw_error(env, NULL, "Failed to write headers"); 773 774 return nullptr; 775 } 776 777 778 napi_value 779 Unit::response_write(napi_env env, napi_callback_info info) 780 { 781 int ret; 782 char *ptr; 783 size_t argc, have_buf_len; 784 int64_t req_p; 785 uint32_t buf_len; 786 napi_value this_arg, req_num; 787 napi_status status; 788 nxt_unit_buf_t *buf; 789 napi_valuetype buf_type; 790 nxt_unit_request_info_t *req; 791 napi_value argv[3]; 792 793 argc = 3; 794 795 status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL); 796 if (status != napi_ok) { 797 goto failed; 798 } 799 800 if (argc != 3) { 801 napi_throw_error(env, NULL, "Wrong args count. Need two: " 802 "chunk, chunk length"); 803 return nullptr; 804 } 805 806 status = napi_get_named_property(env, argv[0], "_req_point", &req_num); 807 if (status != napi_ok) { 808 napi_throw_error(env, NULL, "Failed to get request pointer"); 809 return nullptr; 810 } 811 812 status = napi_get_value_int64(env, req_num, &req_p); 813 if (status != napi_ok) { 814 napi_throw_error(env, NULL, "Failed to get request pointer"); 815 return nullptr; 816 } 817 818 req = (nxt_unit_request_info_t *) (uintptr_t) req_p; 819 820 status = napi_get_value_uint32(env, argv[2], &buf_len); 821 if (status != napi_ok) { 822 goto failed; 823 } 824 825 status = napi_typeof(env, argv[1], &buf_type); 826 if (status != napi_ok) { 827 goto failed; 828 } 829 830 buf_len++; 831 832 buf = nxt_unit_response_buf_alloc(req, buf_len); 833 if (buf == NULL) { 834 goto failed; 835 } 836 837 if (buf_type == napi_string) { 838 /* TODO: will work only for utf8 content-type */ 839 840 status = napi_get_value_string_utf8(env, argv[1], buf->free, 841 buf_len, &have_buf_len); 842 843 } else { 844 status = napi_get_buffer_info(env, argv[1], (void **) &ptr, 845 &have_buf_len); 846 847 memcpy(buf->free, ptr, have_buf_len); 848 } 849 850 if (status != napi_ok) { 851 goto failed; 852 } 853 854 buf->free += have_buf_len; 855 856 ret = nxt_unit_buf_send(buf); 857 if (ret != NXT_UNIT_OK) { 858 goto failed; 859 } 860 861 return this_arg; 862 863 failed: 864 865 napi_throw_error(env, NULL, "Failed to write body"); 866 867 return nullptr; 868 } 869 870 871 napi_value 872 Unit::response_end(napi_env env, napi_callback_info info) 873 { 874 size_t argc; 875 int64_t req_p; 876 napi_value resp, this_arg, req_num; 877 napi_status status; 878 nxt_unit_request_info_t *req; 879 880 argc = 1; 881 882 status = napi_get_cb_info(env, info, &argc, &resp, &this_arg, NULL); 883 if (status != napi_ok) { 884 napi_throw_error(env, NULL, "Failed to finalize sending body"); 885 return nullptr; 886 } 887 888 status = napi_get_named_property(env, resp, "_req_point", &req_num); 889 if (status != napi_ok) { 890 napi_throw_error(env, NULL, "Failed to get request pointer"); 891 return nullptr; 892 } 893 894 status = napi_get_value_int64(env, req_num, &req_p); 895 if (status != napi_ok) { 896 napi_throw_error(env, NULL, "Failed to get request pointer"); 897 return nullptr; 898 } 899 900 req = (nxt_unit_request_info_t *) (uintptr_t) req_p; 901 902 nxt_unit_request_done(req, NXT_UNIT_OK); 903 904 return this_arg; 905 } 906