xref: /unit/src/nodejs/unit-http/unit.cpp (revision 871:3c679d901524)
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;
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, &value);
818         if (status != napi_ok) {
819             goto failed;
820         }
821 
822         status = napi_get_value_string_latin1(env, name, ptr, header_len,
823                                               &name_len);
824         if (status != napi_ok) {
825             goto failed;
826         }
827 
828         name_ptr = ptr;
829 
830         ptr += name_len;
831         header_len -= name_len;
832 
833         hash = nxt_unit_field_hash(name_ptr, name_len);
834 
835         status = napi_is_array(env, value, &is_array);
836         if (status != napi_ok) {
837             goto failed;
838         }
839 
840         if (is_array) {
841             status = napi_get_array_length(env, value, &array_len);
842             if (status != napi_ok) {
843                 goto failed;
844             }
845 
846             for (j = 0; j < array_len; j++) {
847                 status = napi_get_element(env, value, j, &array_val);
848                 if (status != napi_ok) {
849                     goto failed;
850                 }
851 
852                 napi_typeof(env, array_val, &val_type);
853                 if (status != napi_ok) {
854                     goto failed;
855                 }
856 
857                 if (val_type != napi_string) {
858                     status = napi_coerce_to_string(env, array_val, &array_val);
859                     if (status != napi_ok) {
860                         goto failed;
861                     }
862                 }
863 
864                 status = napi_get_value_string_latin1(env, array_val, ptr,
865                                                       header_len,
866                                                       &value_len);
867                 if (status != napi_ok) {
868                     goto failed;
869                 }
870 
871                 f = req->response->fields + req->response->fields_count;
872                 f->skip = 0;
873 
874                 nxt_unit_sptr_set(&f->name, name_ptr);
875 
876                 f->name_length = name_len;
877                 f->hash = hash;
878 
879                 nxt_unit_sptr_set(&f->value, ptr);
880                 f->value_length = (uint32_t) value_len;
881 
882                 ptr += value_len;
883                 header_len -= value_len;
884 
885                 req->response->fields_count++;
886             }
887 
888         } else {
889             napi_typeof(env, value, &val_type);
890             if (status != napi_ok) {
891                 goto failed;
892             }
893 
894             if (val_type != napi_string) {
895                 status = napi_coerce_to_string(env, value, &value);
896                 if (status != napi_ok) {
897                     goto failed;
898                 }
899             }
900 
901             status = napi_get_value_string_latin1(env, value, ptr, header_len,
902                                                   &value_len);
903             if (status != napi_ok) {
904                 goto failed;
905             }
906 
907             f = req->response->fields + req->response->fields_count;
908             f->skip = 0;
909 
910             nxt_unit_sptr_set(&f->name, name_ptr);
911 
912             f->name_length = name_len;
913             f->hash = hash;
914 
915             nxt_unit_sptr_set(&f->value, ptr);
916             f->value_length = (uint32_t) value_len;
917 
918             ptr += value_len;
919             header_len -= value_len;
920 
921             req->response->fields_count++;
922         }
923     }
924 
925     req->response_buf->free = ptr;
926 
927     ret = nxt_unit_response_send(req);
928     if (ret != NXT_UNIT_OK) {
929         goto failed;
930     }
931 
932     return this_arg;
933 
934 failed:
935 
936     req->response->fields_count = 0;
937 
938     napi_throw_error(env, NULL, "Failed to write headers");
939 
940     return nullptr;
941 }
942 
943 
944 napi_value
945 Unit::response_write(napi_env env, napi_callback_info info)
946 {
947     int                      ret;
948     char                     *ptr;
949     size_t                   argc, have_buf_len;
950     int64_t                  req_p;
951     uint32_t                 buf_len;
952     napi_value               this_arg, req_num;
953     napi_status              status;
954     nxt_unit_buf_t           *buf;
955     napi_valuetype           buf_type;
956     nxt_unit_request_info_t  *req;
957     napi_value               argv[3];
958 
959     argc = 3;
960 
961     status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL);
962     if (status != napi_ok) {
963         goto failed;
964     }
965 
966     if (argc != 3) {
967         napi_throw_error(env, NULL, "Wrong args count. Need two: "
968                          "chunk, chunk length");
969         return nullptr;
970     }
971 
972     status = napi_get_named_property(env, argv[0], "_req_point", &req_num);
973     if (status != napi_ok) {
974         napi_throw_error(env, NULL, "Failed to get request pointer");
975         return nullptr;
976     }
977 
978     status = napi_get_value_int64(env, req_num, &req_p);
979     if (status != napi_ok) {
980         napi_throw_error(env, NULL, "Failed to get request pointer");
981         return nullptr;
982     }
983 
984     req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
985 
986     status = napi_get_value_uint32(env, argv[2], &buf_len);
987     if (status != napi_ok) {
988         goto failed;
989     }
990 
991     status = napi_typeof(env, argv[1], &buf_type);
992     if (status != napi_ok) {
993         goto failed;
994     }
995 
996     buf_len++;
997 
998     buf = nxt_unit_response_buf_alloc(req, buf_len);
999     if (buf == NULL) {
1000         goto failed;
1001     }
1002 
1003     if (buf_type == napi_string) {
1004         /* TODO: will work only for utf8 content-type */
1005 
1006         status = napi_get_value_string_utf8(env, argv[1], buf->free,
1007                                             buf_len, &have_buf_len);
1008 
1009     } else {
1010         status = napi_get_buffer_info(env, argv[1], (void **) &ptr,
1011                                       &have_buf_len);
1012 
1013         memcpy(buf->free, ptr, have_buf_len);
1014     }
1015 
1016     if (status != napi_ok) {
1017         goto failed;
1018     }
1019 
1020     buf->free += have_buf_len;
1021 
1022     ret = nxt_unit_buf_send(buf);
1023     if (ret != NXT_UNIT_OK) {
1024         goto failed;
1025     }
1026 
1027     return this_arg;
1028 
1029 failed:
1030 
1031     napi_throw_error(env, NULL, "Failed to write body");
1032 
1033     return nullptr;
1034 }
1035 
1036 
1037 napi_value
1038 Unit::response_end(napi_env env, napi_callback_info info)
1039 {
1040     size_t                   argc;
1041     int64_t                  req_p;
1042     napi_value               resp, this_arg, req_num;
1043     napi_status              status;
1044     nxt_unit_request_info_t  *req;
1045 
1046     argc = 1;
1047 
1048     status = napi_get_cb_info(env, info, &argc, &resp, &this_arg, NULL);
1049     if (status != napi_ok) {
1050         napi_throw_error(env, NULL, "Failed to finalize sending body");
1051         return nullptr;
1052     }
1053 
1054     status = napi_get_named_property(env, resp, "_req_point", &req_num);
1055     if (status != napi_ok) {
1056         napi_throw_error(env, NULL, "Failed to get request pointer");
1057         return nullptr;
1058     }
1059 
1060     status = napi_get_value_int64(env, req_num, &req_p);
1061     if (status != napi_ok) {
1062         napi_throw_error(env, NULL, "Failed to get request pointer");
1063         return nullptr;
1064     }
1065 
1066     req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
1067 
1068     nxt_unit_request_done(req, NXT_UNIT_OK);
1069 
1070     return this_arg;
1071 }
1072