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