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