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