xref: /unit/src/nodejs/unit-http/unit.cpp (revision 875:dae402cb243f)
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