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