xref: /unit/src/nodejs/unit-http/unit.cpp (revision 842:8c77d7fdf952)
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     nxt_unit_field_t         *f;
700     nxt_unit_request_info_t  *req;
701     napi_value               argv[5];
702 
703     argc = 5;
704 
705     status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL);
706     if (status != napi_ok) {
707         return nullptr;
708     }
709 
710     if (argc != 5) {
711         napi_throw_error(env, NULL, "Wrong args count. Need three: "
712                          "statusCode, headers, headers count, headers length");
713         return nullptr;
714     }
715 
716     status = napi_get_named_property(env, argv[0], "_req_point", &req_num);
717     if (status != napi_ok) {
718         napi_throw_error(env, NULL, "Failed to get request pointer");
719         return nullptr;
720     }
721 
722     status = napi_get_value_int64(env, req_num, &req_p);
723     if (status != napi_ok) {
724         napi_throw_error(env, NULL, "Failed to get request pointer");
725         return nullptr;
726     }
727 
728     req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
729 
730     status = napi_get_value_uint32(env, argv[1], &status_code);
731     if (status != napi_ok) {
732         goto failed;
733     }
734 
735     status = napi_get_value_uint32(env, argv[3], &keys_count);
736     if (status != napi_ok) {
737         goto failed;
738     }
739 
740     status = napi_get_value_uint32(env, argv[4], &header_len);
741     if (status != napi_ok) {
742         goto failed;
743     }
744 
745     /* Need to reserve extra byte for C-string 0-termination. */
746     header_len++;
747 
748     headers = argv[2];
749 
750     ret = nxt_unit_response_init(req, status_code, keys_count, header_len);
751     if (ret != NXT_UNIT_OK) {
752         goto failed;
753     }
754 
755     status = napi_get_property_names(env, headers, &keys);
756     if (status != napi_ok) {
757         goto failed;
758     }
759 
760     status = napi_get_array_length(env, keys, &keys_len);
761     if (status != napi_ok) {
762         goto failed;
763     }
764 
765     ptr = req->response_buf->free;
766 
767     for (i = 0; i < keys_len; i++) {
768         status = napi_get_element(env, keys, i, &name);
769         if (status != napi_ok) {
770             goto failed;
771         }
772 
773         status = napi_get_property(env, headers, name, &value);
774         if (status != napi_ok) {
775             goto failed;
776         }
777 
778         status = napi_get_value_string_latin1(env, name, ptr, header_len,
779                                               &name_len);
780         if (status != napi_ok) {
781             goto failed;
782         }
783 
784         name_ptr = ptr;
785 
786         ptr += name_len;
787         header_len -= name_len;
788 
789         hash = nxt_unit_field_hash(name_ptr, name_len);
790 
791         status = napi_is_array(env, value, &is_array);
792         if (status != napi_ok) {
793             goto failed;
794         }
795 
796         if (is_array) {
797             status = napi_get_array_length(env, value, &array_len);
798             if (status != napi_ok) {
799                 goto failed;
800             }
801 
802             for (j = 0; j < array_len; j++) {
803                 status = napi_get_element(env, value, j, &array_val);
804                 if (status != napi_ok) {
805                     goto failed;
806                 }
807 
808                 status = napi_get_value_string_latin1(env, array_val, ptr,
809                                                       header_len,
810                                                       &value_len);
811                 if (status != napi_ok) {
812                     goto failed;
813                 }
814 
815                 f = req->response->fields + req->response->fields_count;
816                 f->skip = 0;
817 
818                 nxt_unit_sptr_set(&f->name, name_ptr);
819 
820                 f->name_length = name_len;
821                 f->hash = hash;
822 
823                 nxt_unit_sptr_set(&f->value, ptr);
824                 f->value_length = (uint32_t) value_len;
825 
826                 ptr += value_len;
827                 header_len -= value_len;
828 
829                 req->response->fields_count++;
830             }
831 
832         } else {
833             status = napi_get_value_string_latin1(env, value, ptr, header_len,
834                                                   &value_len);
835             if (status != napi_ok) {
836                 goto failed;
837             }
838 
839             f = req->response->fields + req->response->fields_count;
840             f->skip = 0;
841 
842             nxt_unit_sptr_set(&f->name, name_ptr);
843 
844             f->name_length = name_len;
845             f->hash = hash;
846 
847             nxt_unit_sptr_set(&f->value, ptr);
848             f->value_length = (uint32_t) value_len;
849 
850             ptr += value_len;
851             header_len -= value_len;
852 
853             req->response->fields_count++;
854         }
855     }
856 
857     req->response_buf->free = ptr;
858 
859     ret = nxt_unit_response_send(req);
860     if (ret != NXT_UNIT_OK) {
861         goto failed;
862     }
863 
864     return this_arg;
865 
866 failed:
867 
868     req->response->fields_count = 0;
869 
870     napi_throw_error(env, NULL, "Failed to write headers");
871 
872     return nullptr;
873 }
874 
875 
876 napi_value
877 Unit::response_write(napi_env env, napi_callback_info info)
878 {
879     int                      ret;
880     char                     *ptr;
881     size_t                   argc, have_buf_len;
882     int64_t                  req_p;
883     uint32_t                 buf_len;
884     napi_value               this_arg, req_num;
885     napi_status              status;
886     nxt_unit_buf_t           *buf;
887     napi_valuetype           buf_type;
888     nxt_unit_request_info_t  *req;
889     napi_value               argv[3];
890 
891     argc = 3;
892 
893     status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL);
894     if (status != napi_ok) {
895         goto failed;
896     }
897 
898     if (argc != 3) {
899         napi_throw_error(env, NULL, "Wrong args count. Need two: "
900                          "chunk, chunk length");
901         return nullptr;
902     }
903 
904     status = napi_get_named_property(env, argv[0], "_req_point", &req_num);
905     if (status != napi_ok) {
906         napi_throw_error(env, NULL, "Failed to get request pointer");
907         return nullptr;
908     }
909 
910     status = napi_get_value_int64(env, req_num, &req_p);
911     if (status != napi_ok) {
912         napi_throw_error(env, NULL, "Failed to get request pointer");
913         return nullptr;
914     }
915 
916     req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
917 
918     status = napi_get_value_uint32(env, argv[2], &buf_len);
919     if (status != napi_ok) {
920         goto failed;
921     }
922 
923     status = napi_typeof(env, argv[1], &buf_type);
924     if (status != napi_ok) {
925         goto failed;
926     }
927 
928     buf_len++;
929 
930     buf = nxt_unit_response_buf_alloc(req, buf_len);
931     if (buf == NULL) {
932         goto failed;
933     }
934 
935     if (buf_type == napi_string) {
936         /* TODO: will work only for utf8 content-type */
937 
938         status = napi_get_value_string_utf8(env, argv[1], buf->free,
939                                             buf_len, &have_buf_len);
940 
941     } else {
942         status = napi_get_buffer_info(env, argv[1], (void **) &ptr,
943                                       &have_buf_len);
944 
945         memcpy(buf->free, ptr, have_buf_len);
946     }
947 
948     if (status != napi_ok) {
949         goto failed;
950     }
951 
952     buf->free += have_buf_len;
953 
954     ret = nxt_unit_buf_send(buf);
955     if (ret != NXT_UNIT_OK) {
956         goto failed;
957     }
958 
959     return this_arg;
960 
961 failed:
962 
963     napi_throw_error(env, NULL, "Failed to write body");
964 
965     return nullptr;
966 }
967 
968 
969 napi_value
970 Unit::response_end(napi_env env, napi_callback_info info)
971 {
972     size_t                   argc;
973     int64_t                  req_p;
974     napi_value               resp, this_arg, req_num;
975     napi_status              status;
976     nxt_unit_request_info_t  *req;
977 
978     argc = 1;
979 
980     status = napi_get_cb_info(env, info, &argc, &resp, &this_arg, NULL);
981     if (status != napi_ok) {
982         napi_throw_error(env, NULL, "Failed to finalize sending body");
983         return nullptr;
984     }
985 
986     status = napi_get_named_property(env, resp, "_req_point", &req_num);
987     if (status != napi_ok) {
988         napi_throw_error(env, NULL, "Failed to get request pointer");
989         return nullptr;
990     }
991 
992     status = napi_get_value_int64(env, req_num, &req_p);
993     if (status != napi_ok) {
994         napi_throw_error(env, NULL, "Failed to get request pointer");
995         return nullptr;
996     }
997 
998     req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
999 
1000     nxt_unit_request_done(req, NXT_UNIT_OK);
1001 
1002     return this_arg;
1003 }
1004