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, global, server_obj, except; 281 napi_value emit_events, events_res, async_name, resource_object; 282 napi_status status; 283 napi_async_context async_context; 284 napi_callback_scope async_scope; 285 napi_value events_args[3]; 286 287 obj = reinterpret_cast<Unit *>(req->unit->data); 288 289 napi_handle_scope scope; 290 status = napi_open_handle_scope(obj->env_, &scope); 291 if (status != napi_ok) { 292 napi_throw_error(obj->env_, NULL, "Failed to create handle scope"); 293 return; 294 } 295 296 server_obj = obj->get_server_object(); 297 if (server_obj == nullptr) { 298 napi_throw_error(obj->env_, NULL, "Failed to get server object"); 299 return; 300 } 301 302 status = napi_get_global(obj->env_, &global); 303 if (status != napi_ok) { 304 napi_throw_error(obj->env_, NULL, "Failed to get global variable"); 305 return; 306 } 307 308 socket = obj->create_socket(server_obj, req); 309 if (socket == nullptr) { 310 napi_throw_error(obj->env_, NULL, "Failed to create socket object"); 311 return; 312 } 313 314 request = obj->create_request(server_obj, socket); 315 if (request == nullptr) { 316 napi_throw_error(obj->env_, NULL, "Failed to create request object"); 317 return; 318 } 319 320 response = obj->create_response(server_obj, socket, request, req, obj); 321 if (response == nullptr) { 322 napi_throw_error(obj->env_, NULL, "Failed to create response object"); 323 return; 324 } 325 326 status = obj->create_headers(req, request); 327 if (status != napi_ok) { 328 napi_throw_error(obj->env_, NULL, "Failed to create headers"); 329 return; 330 } 331 332 status = napi_get_named_property(obj->env_, server_obj, "emit_events", 333 &emit_events); 334 if (status != napi_ok) { 335 napi_throw_error(obj->env_, NULL, "Failed to get " 336 "'emit_events' function"); 337 return; 338 } 339 340 events_args[0] = server_obj; 341 events_args[1] = request; 342 events_args[2] = response; 343 344 status = napi_create_string_utf8(obj->env_, "unit_request_handler", 345 sizeof("unit_request_handler") - 1, 346 &async_name); 347 if (status != napi_ok) { 348 napi_throw_error(obj->env_, NULL, "Failed to create utf-8 string"); 349 return; 350 } 351 352 status = napi_async_init(obj->env_, NULL, async_name, &async_context); 353 if (status != napi_ok) { 354 napi_throw_error(obj->env_, NULL, "Failed to init async object"); 355 return; 356 } 357 358 status = napi_create_object(obj->env_, &resource_object); 359 if (status != napi_ok) { 360 napi_throw_error(obj->env_, NULL, "Failed to create object for " 361 "callback scope"); 362 return; 363 } 364 365 status = napi_open_callback_scope(obj->env_, resource_object, async_context, 366 &async_scope); 367 if (status != napi_ok) { 368 napi_throw_error(obj->env_, NULL, "Failed to open callback scope"); 369 return; 370 } 371 372 status = napi_make_callback(obj->env_, async_context, server_obj, 373 emit_events, 3, events_args, &events_res); 374 if (status != napi_ok) { 375 if (status != napi_pending_exception) { 376 napi_throw_error(obj->env_, NULL, "Failed to make callback"); 377 return; 378 } 379 380 status = napi_get_and_clear_last_exception(obj->env_, &except); 381 if (status != napi_ok) { 382 napi_throw_error(obj->env_, NULL, 383 "Failed to get and clear last exception"); 384 return; 385 } 386 387 /* Logging a description of the error and call stack. */ 388 status = napi_fatal_exception(obj->env_, except); 389 if (status != napi_ok) { 390 napi_throw_error(obj->env_, NULL, "Failed to call " 391 "napi_fatal_exception() function"); 392 return; 393 } 394 } 395 396 status = napi_close_callback_scope(obj->env_, async_scope); 397 if (status != napi_ok) { 398 napi_throw_error(obj->env_, NULL, "Failed to close callback scope"); 399 return; 400 } 401 402 status = napi_async_destroy(obj->env_, async_context); 403 if (status != napi_ok) { 404 napi_throw_error(obj->env_, NULL, "Failed to destroy async object"); 405 return; 406 } 407 408 status = napi_close_handle_scope(obj->env_, scope); 409 if (status != napi_ok) { 410 napi_throw_error(obj->env_, NULL, "Failed to close handle scope"); 411 } 412 } 413 414 415 void 416 nxt_uv_read_callback(uv_poll_t *handle, int status, int events) 417 { 418 nxt_unit_run_once((nxt_unit_ctx_t *) handle->data); 419 } 420 421 422 int 423 Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) 424 { 425 int err; 426 Unit *obj; 427 uv_loop_t *loop; 428 napi_status status; 429 nxt_nodejs_ctx_t *node_ctx; 430 431 if (port->in_fd != -1) { 432 obj = reinterpret_cast<Unit *>(ctx->unit->data); 433 434 if (fcntl(port->in_fd, F_SETFL, O_NONBLOCK) == -1) { 435 napi_throw_error(obj->env_, NULL, "Failed to upgrade read" 436 " file descriptor to O_NONBLOCK"); 437 return -1; 438 } 439 440 status = napi_get_uv_event_loop(obj->env_, &loop); 441 if (status != napi_ok) { 442 napi_throw_error(obj->env_, NULL, "Failed to get uv.loop"); 443 return NXT_UNIT_ERROR; 444 } 445 446 node_ctx = new nxt_nodejs_ctx_t; 447 448 err = uv_poll_init(loop, &node_ctx->poll, port->in_fd); 449 if (err < 0) { 450 napi_throw_error(obj->env_, NULL, "Failed to init uv.poll"); 451 return NXT_UNIT_ERROR; 452 } 453 454 err = uv_poll_start(&node_ctx->poll, UV_READABLE, nxt_uv_read_callback); 455 if (err < 0) { 456 napi_throw_error(obj->env_, NULL, "Failed to start uv.poll"); 457 return NXT_UNIT_ERROR; 458 } 459 460 ctx->data = node_ctx; 461 462 node_ctx->port_id = port->id; 463 node_ctx->poll.data = ctx; 464 } 465 466 return nxt_unit_add_port(ctx, port); 467 } 468 469 470 inline bool 471 operator == (const nxt_unit_port_id_t &p1, const nxt_unit_port_id_t &p2) 472 { 473 return p1.pid == p2.pid && p1.id == p2.id; 474 } 475 476 477 void 478 Unit::remove_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id) 479 { 480 nxt_nodejs_ctx_t *node_ctx; 481 482 if (ctx->data != NULL) { 483 node_ctx = (nxt_nodejs_ctx_t *) ctx->data; 484 485 if (node_ctx->port_id == *port_id) { 486 uv_poll_stop(&node_ctx->poll); 487 488 delete node_ctx; 489 490 ctx->data = NULL; 491 } 492 } 493 494 nxt_unit_remove_port(ctx, port_id); 495 } 496 497 498 void 499 Unit::quit(nxt_unit_ctx_t *ctx) 500 { 501 nxt_unit_done(ctx); 502 } 503 504 505 napi_value 506 Unit::get_server_object() 507 { 508 napi_value unit_obj, server_obj; 509 napi_status status; 510 511 status = napi_get_reference_value(env_, wrapper_, &unit_obj); 512 if (status != napi_ok) { 513 return nullptr; 514 } 515 516 status = napi_get_named_property(env_, unit_obj, "server", &server_obj); 517 if (status != napi_ok) { 518 return nullptr; 519 } 520 521 return server_obj; 522 } 523 524 525 napi_status 526 Unit::create_headers(nxt_unit_request_info_t *req, napi_value request) 527 { 528 uint32_t i; 529 const char *p; 530 napi_value headers, raw_headers, str; 531 napi_status status; 532 nxt_unit_field_t *f; 533 nxt_unit_request_t *r; 534 535 r = req->request; 536 537 status = napi_create_object(env_, &headers); 538 if (status != napi_ok) { 539 return status; 540 } 541 542 status = napi_create_array_with_length(env_, r->fields_count * 2, 543 &raw_headers); 544 if (status != napi_ok) { 545 return status; 546 } 547 548 for (i = 0; i < r->fields_count; i++) { 549 f = r->fields + i; 550 551 status = this->append_header(f, headers, raw_headers, i); 552 if (status != napi_ok) { 553 return status; 554 } 555 } 556 557 status = napi_set_named_property(env_, request, "headers", headers); 558 if (status != napi_ok) { 559 return status; 560 } 561 562 status = napi_set_named_property(env_, request, "rawHeaders", raw_headers); 563 if (status != napi_ok) { 564 return status; 565 } 566 567 p = (const char *) nxt_unit_sptr_get(&r->version); 568 569 status = napi_create_string_latin1(env_, p, r->version_length, &str); 570 if (status != napi_ok) { 571 return status; 572 } 573 574 status = napi_set_named_property(env_, request, "httpVersion", str); 575 if (status != napi_ok) { 576 return status; 577 } 578 579 p = (const char *) nxt_unit_sptr_get(&r->method); 580 581 status = napi_create_string_latin1(env_, p, r->method_length, &str); 582 if (status != napi_ok) { 583 return status; 584 } 585 586 status = napi_set_named_property(env_, request, "method", str); 587 if (status != napi_ok) { 588 return status; 589 } 590 591 p = (const char *) nxt_unit_sptr_get(&r->target); 592 593 status = napi_create_string_latin1(env_, p, r->target_length, &str); 594 if (status != napi_ok) { 595 return status; 596 } 597 598 status = napi_set_named_property(env_, request, "url", str); 599 if (status != napi_ok) { 600 return status; 601 } 602 603 return napi_ok; 604 } 605 606 607 inline napi_status 608 Unit::append_header(nxt_unit_field_t *f, napi_value headers, 609 napi_value raw_headers, uint32_t idx) 610 { 611 const char *name, *value; 612 napi_value str, vstr; 613 napi_status status; 614 615 value = (const char *) nxt_unit_sptr_get(&f->value); 616 617 status = napi_create_string_latin1(env_, value, f->value_length, &vstr); 618 if (status != napi_ok) { 619 return status; 620 } 621 622 name = (const char *) nxt_unit_sptr_get(&f->name); 623 624 status = napi_set_named_property(env_, headers, name, vstr); 625 if (status != napi_ok) { 626 return status; 627 } 628 629 status = napi_create_string_latin1(env_, name, f->name_length, &str); 630 if (status != napi_ok) { 631 return status; 632 } 633 634 status = napi_set_element(env_, raw_headers, idx * 2, str); 635 if (status != napi_ok) { 636 return status; 637 } 638 639 status = napi_set_element(env_, raw_headers, idx * 2 + 1, vstr); 640 if (status != napi_ok) { 641 return status; 642 } 643 644 return napi_ok; 645 } 646 647 648 napi_value 649 Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req) 650 { 651 napi_value constructor, return_val, req_pointer; 652 napi_status status; 653 654 status = napi_get_named_property(env_, server_obj, "socket", 655 &constructor); 656 if (status != napi_ok) { 657 return nullptr; 658 } 659 660 status = napi_new_instance(env_, constructor, 0, NULL, &return_val); 661 if (status != napi_ok) { 662 return nullptr; 663 } 664 665 status = napi_create_int64(env_, (uintptr_t) req, &req_pointer); 666 if (status != napi_ok) { 667 return nullptr; 668 } 669 670 status = napi_set_named_property(env_, return_val, "req_pointer", 671 req_pointer); 672 if (status != napi_ok) { 673 return nullptr; 674 } 675 676 return return_val; 677 } 678 679 680 napi_value 681 Unit::create_request(napi_value server_obj, napi_value socket) 682 { 683 napi_value constructor, return_val; 684 napi_status status; 685 686 status = napi_get_named_property(env_, server_obj, "request", 687 &constructor); 688 if (status != napi_ok) { 689 return nullptr; 690 } 691 692 status = napi_new_instance(env_, constructor, 1, &server_obj, 693 &return_val); 694 if (status != napi_ok) { 695 return nullptr; 696 } 697 698 status = napi_set_named_property(env_, return_val, "socket", socket); 699 if (status != napi_ok) { 700 return nullptr; 701 } 702 703 return return_val; 704 } 705 706 707 napi_value 708 Unit::create_response(napi_value server_obj, napi_value socket, 709 napi_value request, nxt_unit_request_info_t *req, 710 Unit *obj) 711 { 712 napi_value constructor, return_val, req_num; 713 napi_status status; 714 715 status = napi_get_named_property(env_, server_obj, "response", 716 &constructor); 717 if (status != napi_ok) { 718 return nullptr; 719 } 720 721 status = napi_new_instance(env_, constructor, 1, &request, &return_val); 722 if (status != napi_ok) { 723 return nullptr; 724 } 725 726 status = napi_set_named_property(env_, return_val, "socket", socket); 727 if (status != napi_ok) { 728 return nullptr; 729 } 730 731 status = napi_create_int64(env_, (int64_t) (uintptr_t) req, &req_num); 732 if (status != napi_ok) { 733 return nullptr; 734 } 735 736 status = napi_set_named_property(env_, return_val, "_req_point", req_num); 737 if (status != napi_ok) { 738 return nullptr; 739 } 740 741 return return_val; 742 } 743 744 745 napi_value 746 Unit::response_send_headers(napi_env env, napi_callback_info info) 747 { 748 int ret; 749 char *ptr, *name_ptr; 750 bool is_array; 751 size_t argc, name_len, value_len; 752 int64_t req_p; 753 uint32_t status_code, header_len, keys_len, array_len; 754 uint32_t keys_count, i, j; 755 uint16_t hash; 756 napi_value this_arg, headers, keys, name, value, array_val; 757 napi_value req_num, array_entry; 758 napi_status status; 759 napi_valuetype val_type; 760 nxt_unit_field_t *f; 761 nxt_unit_request_info_t *req; 762 napi_value argv[5]; 763 764 argc = 5; 765 766 status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL); 767 if (status != napi_ok) { 768 return nullptr; 769 } 770 771 if (argc != 5) { 772 napi_throw_error(env, NULL, "Wrong args count. Need three: " 773 "statusCode, headers, headers count, headers length"); 774 return nullptr; 775 } 776 777 status = napi_get_named_property(env, argv[0], "_req_point", &req_num); 778 if (status != napi_ok) { 779 napi_throw_error(env, NULL, "Failed to get request pointer"); 780 return nullptr; 781 } 782 783 status = napi_get_value_int64(env, req_num, &req_p); 784 if (status != napi_ok) { 785 napi_throw_error(env, NULL, "Failed to get request pointer"); 786 return nullptr; 787 } 788 789 req = (nxt_unit_request_info_t *) (uintptr_t) req_p; 790 791 status = napi_get_value_uint32(env, argv[1], &status_code); 792 if (status != napi_ok) { 793 goto failed; 794 } 795 796 status = napi_get_value_uint32(env, argv[3], &keys_count); 797 if (status != napi_ok) { 798 goto failed; 799 } 800 801 status = napi_get_value_uint32(env, argv[4], &header_len); 802 if (status != napi_ok) { 803 goto failed; 804 } 805 806 /* Need to reserve extra byte for C-string 0-termination. */ 807 header_len++; 808 809 headers = argv[2]; 810 811 ret = nxt_unit_response_init(req, status_code, keys_count, header_len); 812 if (ret != NXT_UNIT_OK) { 813 goto failed; 814 } 815 816 status = napi_get_property_names(env, headers, &keys); 817 if (status != napi_ok) { 818 goto failed; 819 } 820 821 status = napi_get_array_length(env, keys, &keys_len); 822 if (status != napi_ok) { 823 goto failed; 824 } 825 826 ptr = req->response_buf->free; 827 828 for (i = 0; i < keys_len; i++) { 829 status = napi_get_element(env, keys, i, &name); 830 if (status != napi_ok) { 831 goto failed; 832 } 833 834 status = napi_get_property(env, headers, name, &array_entry); 835 if (status != napi_ok) { 836 goto failed; 837 } 838 839 status = napi_get_element(env, array_entry, 0, &name); 840 if (status != napi_ok) { 841 goto failed; 842 } 843 844 status = napi_get_element(env, array_entry, 1, &value); 845 if (status != napi_ok) { 846 goto failed; 847 } 848 849 status = napi_get_value_string_latin1(env, name, ptr, header_len, 850 &name_len); 851 if (status != napi_ok) { 852 goto failed; 853 } 854 855 name_ptr = ptr; 856 857 ptr += name_len; 858 header_len -= name_len; 859 860 hash = nxt_unit_field_hash(name_ptr, name_len); 861 862 status = napi_is_array(env, value, &is_array); 863 if (status != napi_ok) { 864 goto failed; 865 } 866 867 if (is_array) { 868 status = napi_get_array_length(env, value, &array_len); 869 if (status != napi_ok) { 870 goto failed; 871 } 872 873 for (j = 0; j < array_len; j++) { 874 status = napi_get_element(env, value, j, &array_val); 875 if (status != napi_ok) { 876 goto failed; 877 } 878 879 napi_typeof(env, array_val, &val_type); 880 if (status != napi_ok) { 881 goto failed; 882 } 883 884 if (val_type != napi_string) { 885 status = napi_coerce_to_string(env, array_val, &array_val); 886 if (status != napi_ok) { 887 goto failed; 888 } 889 } 890 891 status = napi_get_value_string_latin1(env, array_val, ptr, 892 header_len, 893 &value_len); 894 if (status != napi_ok) { 895 goto failed; 896 } 897 898 f = req->response->fields + req->response->fields_count; 899 f->skip = 0; 900 901 nxt_unit_sptr_set(&f->name, name_ptr); 902 903 f->name_length = name_len; 904 f->hash = hash; 905 906 nxt_unit_sptr_set(&f->value, ptr); 907 f->value_length = (uint32_t) value_len; 908 909 ptr += value_len; 910 header_len -= value_len; 911 912 req->response->fields_count++; 913 } 914 915 } else { 916 napi_typeof(env, value, &val_type); 917 if (status != napi_ok) { 918 goto failed; 919 } 920 921 if (val_type != napi_string) { 922 status = napi_coerce_to_string(env, value, &value); 923 if (status != napi_ok) { 924 goto failed; 925 } 926 } 927 928 status = napi_get_value_string_latin1(env, value, ptr, header_len, 929 &value_len); 930 if (status != napi_ok) { 931 goto failed; 932 } 933 934 f = req->response->fields + req->response->fields_count; 935 f->skip = 0; 936 937 nxt_unit_sptr_set(&f->name, name_ptr); 938 939 f->name_length = name_len; 940 f->hash = hash; 941 942 nxt_unit_sptr_set(&f->value, ptr); 943 f->value_length = (uint32_t) value_len; 944 945 ptr += value_len; 946 header_len -= value_len; 947 948 req->response->fields_count++; 949 } 950 } 951 952 req->response_buf->free = ptr; 953 954 ret = nxt_unit_response_send(req); 955 if (ret != NXT_UNIT_OK) { 956 goto failed; 957 } 958 959 return this_arg; 960 961 failed: 962 963 req->response->fields_count = 0; 964 965 napi_throw_error(env, NULL, "Failed to write headers"); 966 967 return nullptr; 968 } 969 970 971 napi_value 972 Unit::response_write(napi_env env, napi_callback_info info) 973 { 974 int ret; 975 char *ptr; 976 size_t argc, have_buf_len; 977 int64_t req_p; 978 uint32_t buf_len; 979 napi_value this_arg, req_num; 980 napi_status status; 981 nxt_unit_buf_t *buf; 982 napi_valuetype buf_type; 983 nxt_unit_request_info_t *req; 984 napi_value argv[3]; 985 986 argc = 3; 987 988 status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL); 989 if (status != napi_ok) { 990 goto failed; 991 } 992 993 if (argc != 3) { 994 napi_throw_error(env, NULL, "Wrong args count. Need two: " 995 "chunk, chunk length"); 996 return nullptr; 997 } 998 999 status = napi_get_named_property(env, argv[0], "_req_point", &req_num); 1000 if (status != napi_ok) { 1001 napi_throw_error(env, NULL, "Failed to get request pointer"); 1002 return nullptr; 1003 } 1004 1005 status = napi_get_value_int64(env, req_num, &req_p); 1006 if (status != napi_ok) { 1007 napi_throw_error(env, NULL, "Failed to get request pointer"); 1008 return nullptr; 1009 } 1010 1011 req = (nxt_unit_request_info_t *) (uintptr_t) req_p; 1012 1013 status = napi_get_value_uint32(env, argv[2], &buf_len); 1014 if (status != napi_ok) { 1015 goto failed; 1016 } 1017 1018 status = napi_typeof(env, argv[1], &buf_type); 1019 if (status != napi_ok) { 1020 goto failed; 1021 } 1022 1023 buf_len++; 1024 1025 buf = nxt_unit_response_buf_alloc(req, buf_len); 1026 if (buf == NULL) { 1027 goto failed; 1028 } 1029 1030 if (buf_type == napi_string) { 1031 /* TODO: will work only for utf8 content-type */ 1032 1033 status = napi_get_value_string_utf8(env, argv[1], buf->free, 1034 buf_len, &have_buf_len); 1035 1036 } else { 1037 status = napi_get_buffer_info(env, argv[1], (void **) &ptr, 1038 &have_buf_len); 1039 1040 memcpy(buf->free, ptr, have_buf_len); 1041 } 1042 1043 if (status != napi_ok) { 1044 goto failed; 1045 } 1046 1047 buf->free += have_buf_len; 1048 1049 ret = nxt_unit_buf_send(buf); 1050 if (ret != NXT_UNIT_OK) { 1051 goto failed; 1052 } 1053 1054 return this_arg; 1055 1056 failed: 1057 1058 napi_throw_error(env, NULL, "Failed to write body"); 1059 1060 return nullptr; 1061 } 1062 1063 1064 napi_value 1065 Unit::response_end(napi_env env, napi_callback_info info) 1066 { 1067 size_t argc; 1068 int64_t req_p; 1069 napi_value resp, this_arg, req_num; 1070 napi_status status; 1071 nxt_unit_request_info_t *req; 1072 1073 argc = 1; 1074 1075 status = napi_get_cb_info(env, info, &argc, &resp, &this_arg, NULL); 1076 if (status != napi_ok) { 1077 napi_throw_error(env, NULL, "Failed to finalize sending body"); 1078 return nullptr; 1079 } 1080 1081 status = napi_get_named_property(env, resp, "_req_point", &req_num); 1082 if (status != napi_ok) { 1083 napi_throw_error(env, NULL, "Failed to get request pointer"); 1084 return nullptr; 1085 } 1086 1087 status = napi_get_value_int64(env, req_num, &req_p); 1088 if (status != napi_ok) { 1089 napi_throw_error(env, NULL, "Failed to get request pointer"); 1090 return nullptr; 1091 } 1092 1093 req = (nxt_unit_request_info_t *) (uintptr_t) req_p; 1094 1095 nxt_unit_request_done(req, NXT_UNIT_OK); 1096 1097 return this_arg; 1098 } 1099