xref: /unit/src/nodejs/unit-http/unit.cpp (revision 841:96d2b67e456a)
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 Unit::Unit(napi_env env):
18     env_(env),
19     wrapper_(nullptr),
20     unit_ctx_(nullptr)
21 {
22 }
23 
24 
25 Unit::~Unit()
26 {
27     napi_delete_reference(env_, wrapper_);
28 }
29 
30 
31 napi_value
32 Unit::init(napi_env env, napi_value exports)
33 {
34     napi_value   cons, fn;
35     napi_status  status;
36 
37     napi_property_descriptor  properties[] = {
38         { "createServer", 0, create_server, 0, 0, 0, napi_default, 0 },
39         { "listen", 0, listen, 0, 0, 0, napi_default, 0 },
40         { "_read", 0, _read, 0, 0, 0, napi_default, 0 }
41     };
42 
43     status = napi_define_class(env, "Unit", NAPI_AUTO_LENGTH, create, nullptr,
44                                3, properties, &cons);
45     if (status != napi_ok) {
46         goto failed;
47     }
48 
49     status = napi_create_reference(env, cons, 1, &constructor_);
50     if (status != napi_ok) {
51         goto failed;
52     }
53 
54     status = napi_set_named_property(env, exports, "Unit", cons);
55     if (status != napi_ok) {
56         goto failed;
57     }
58 
59     status = napi_create_function(env, NULL, 0, response_send_headers, NULL,
60                                   &fn);
61     if (status != napi_ok) {
62         goto failed;
63     }
64 
65     status = napi_set_named_property(env, exports,
66                                      "unit_response_headers", fn);
67     if (status != napi_ok) {
68         goto failed;
69     }
70 
71     status = napi_create_function(env, NULL, 0, response_write, NULL, &fn);
72     if (status != napi_ok) {
73         goto failed;
74     }
75 
76     status = napi_set_named_property(env, exports, "unit_response_write", fn);
77     if (status != napi_ok) {
78         goto failed;
79     }
80 
81     status = napi_create_function(env, NULL, 0, response_end, NULL, &fn);
82     if (status != napi_ok) {
83         goto failed;
84     }
85 
86     status = napi_set_named_property(env, exports, "unit_response_end", fn);
87     if (status != napi_ok) {
88         goto failed;
89     }
90 
91     return exports;
92 
93 failed:
94 
95     napi_throw_error(env, NULL, "Failed to define Unit class");
96 
97     return nullptr;
98 }
99 
100 
101 void
102 Unit::destroy(napi_env env, void *nativeObject, void *finalize_hint)
103 {
104     Unit  *obj = reinterpret_cast<Unit *>(nativeObject);
105 
106     delete obj;
107 }
108 
109 
110 napi_value
111 Unit::create(napi_env env, napi_callback_info info)
112 {
113     Unit         *obj;
114     napi_ref     ref;
115     napi_value   target, cons, instance, jsthis;
116     napi_status  status;
117 
118     status = napi_get_new_target(env, info, &target);
119     if (status != napi_ok) {
120         goto failed;
121     }
122 
123     if (target != nullptr) {
124         /* Invoked as constructor: `new Unit(...)` */
125         status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis,
126                                   nullptr);
127         if (status != napi_ok) {
128             goto failed;
129         }
130 
131         obj = new Unit(env);
132 
133         status = napi_wrap(env, jsthis, reinterpret_cast<void *>(obj),
134                            destroy, nullptr, &obj->wrapper_);
135         if (status != napi_ok) {
136             goto failed;
137         }
138 
139         status = napi_create_reference(env, jsthis, 1, &ref);
140         if (status != napi_ok) {
141             goto failed;
142         }
143 
144         return jsthis;
145     }
146 
147     /* Invoked as plain function `Unit(...)`, turn into construct call. */
148     status = napi_get_reference_value(env, constructor_, &cons);
149     if (status != napi_ok) {
150         goto failed;
151     }
152 
153     status = napi_new_instance(env, cons, 0, nullptr, &instance);
154     if (status != napi_ok) {
155         goto failed;
156     }
157 
158     status = napi_create_reference(env, instance, 1, &ref);
159     if (status != napi_ok) {
160         goto failed;
161     }
162 
163     return instance;
164 
165 failed:
166 
167     napi_throw_error(env, NULL, "Failed to create Unit object");
168 
169     return nullptr;
170 }
171 
172 
173 napi_value
174 Unit::create_server(napi_env env, napi_callback_info info)
175 {
176     Unit             *obj;
177     size_t           argc;
178     napi_value       jsthis, argv;
179     napi_status      status;
180     nxt_unit_init_t  unit_init;
181 
182     argc = 1;
183 
184     status = napi_get_cb_info(env, info, &argc, &argv, &jsthis, nullptr);
185     if (status != napi_ok) {
186         goto failed;
187     }
188 
189     status = napi_unwrap(env, jsthis, reinterpret_cast<void **>(&obj));
190     if (status != napi_ok) {
191         goto failed;
192     }
193 
194     memset(&unit_init, 0, sizeof(nxt_unit_init_t));
195 
196     unit_init.data = obj;
197     unit_init.callbacks.request_handler = request_handler;
198     unit_init.callbacks.add_port        = add_port;
199     unit_init.callbacks.remove_port     = remove_port;
200 
201     obj->unit_ctx_ = nxt_unit_init(&unit_init);
202     if (obj->unit_ctx_ == NULL) {
203         goto failed;
204     }
205 
206     return nullptr;
207 
208 failed:
209 
210     napi_throw_error(env, NULL, "Failed to create Unit object");
211 
212     return nullptr;
213 }
214 
215 
216 napi_value
217 Unit::listen(napi_env env, napi_callback_info info)
218 {
219     return nullptr;
220 }
221 
222 
223 napi_value
224 Unit::_read(napi_env env, napi_callback_info info)
225 {
226     Unit                     *obj;
227     void                     *data;
228     size_t                   argc;
229     int64_t                  req_pointer;
230     napi_value               jsthis, buffer, argv;
231     napi_status              status;
232     nxt_unit_request_info_t  *req;
233 
234     argc = 1;
235 
236     status = napi_get_cb_info(env, info, &argc, &argv, &jsthis, nullptr);
237     if (status != napi_ok) {
238         napi_throw_error(env, NULL, "Failed to get arguments from js");
239         return nullptr;
240     }
241 
242     status = napi_unwrap(env, jsthis, reinterpret_cast<void **>(&obj));
243     if (status != napi_ok) {
244         napi_throw_error(env, NULL, "Failed to get Unit object form js");
245         return nullptr;
246     }
247 
248     status = napi_get_value_int64(env, argv, &req_pointer);
249     if (status != napi_ok) {
250         napi_throw_error(env, NULL, "Failed to get request pointer");
251         return nullptr;
252     }
253 
254     req = (nxt_unit_request_info_t *) (uintptr_t) req_pointer;
255 
256     status = napi_create_buffer(env, (size_t) req->content_length,
257                                 &data, &buffer);
258     if (status != napi_ok) {
259         napi_throw_error(env, NULL, "Failed to create request buffer");
260         return nullptr;
261     }
262 
263     nxt_unit_request_read(req, data, req->content_length);
264 
265     return buffer;
266 }
267 
268 
269 void
270 Unit::request_handler(nxt_unit_request_info_t *req)
271 {
272     Unit         *obj;
273     napi_value   socket, request, response;
274     napi_value   global, server_obj;
275     napi_value   run_events, events_res;
276     napi_status  status;
277     napi_value   events_args[3];
278 
279     obj = reinterpret_cast<Unit *>(req->unit->data);
280 
281     napi_handle_scope scope;
282     status = napi_open_handle_scope(obj->env_, &scope);
283     if (status != napi_ok) {
284         napi_throw_error(obj->env_, NULL, "Failed to create handle scope");
285         return;
286     }
287 
288     server_obj = obj->get_server_object();
289     if (server_obj == nullptr) {
290         napi_throw_error(obj->env_, NULL, "Failed to get server object");
291         return;
292     }
293 
294     status = napi_get_global(obj->env_, &global);
295     if (status != napi_ok) {
296         napi_throw_error(obj->env_, NULL, "Failed to get global variable");
297         return;
298     }
299 
300     socket = obj->create_socket(server_obj, req);
301     if (socket == nullptr) {
302         napi_throw_error(obj->env_, NULL, "Failed to create socket object");
303         return;
304     }
305 
306     request = obj->create_request(server_obj, socket);
307     if (request == nullptr) {
308         napi_throw_error(obj->env_, NULL, "Failed to create request object");
309         return;
310     }
311 
312     response = obj->create_response(server_obj, socket, request, req, obj);
313     if (response == nullptr) {
314         napi_throw_error(obj->env_, NULL, "Failed to create response object");
315         return;
316     }
317 
318     status = obj->create_headers(req, request);
319     if (status != napi_ok) {
320         napi_throw_error(obj->env_, NULL, "Failed to create headers");
321         return;
322     }
323 
324     status = napi_get_named_property(obj->env_, server_obj, "run_events",
325                                      &run_events);
326     if (status != napi_ok) {
327         napi_throw_error(obj->env_, NULL, "Failed to get"
328                          " 'run_events' function");
329         return;
330     }
331 
332     events_args[0] = server_obj;
333     events_args[1] = request;
334     events_args[2] = response;
335 
336     status = napi_call_function(obj->env_, server_obj, run_events, 3,
337                                 events_args, &events_res);
338     if (status != napi_ok) {
339         napi_throw_error(obj->env_, NULL, "Failed to call"
340                          " 'run_events' function");
341         return;
342     }
343 
344     napi_close_handle_scope(obj->env_, scope);
345 }
346 
347 
348 void
349 nxt_uv_read_callback(uv_poll_t *handle, int status, int events)
350 {
351     nxt_unit_run_once((nxt_unit_ctx_t *) handle->data);
352 }
353 
354 
355 int
356 Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
357 {
358     int          err;
359     Unit         *obj;
360     uv_loop_t    *loop;
361     uv_poll_t    *uv_handle;
362     napi_status  status;
363 
364     if (port->in_fd != -1) {
365         obj = reinterpret_cast<Unit *>(ctx->unit->data);
366 
367         if (fcntl(port->in_fd, F_SETFL, O_NONBLOCK) == -1) {
368             napi_throw_error(obj->env_, NULL, "Failed to upgrade read"
369                              " file descriptor to O_NONBLOCK");
370             return -1;
371         }
372 
373         status = napi_get_uv_event_loop(obj->env_, &loop);
374         if (status != napi_ok) {
375             napi_throw_error(obj->env_, NULL, "Failed to get uv.loop");
376             return NXT_UNIT_ERROR;
377         }
378 
379         uv_handle = new uv_poll_t;
380 
381         err = uv_poll_init(loop, uv_handle, port->in_fd);
382         if (err < 0) {
383             napi_throw_error(obj->env_, NULL, "Failed to init uv.poll");
384             return NXT_UNIT_ERROR;
385         }
386 
387         err = uv_poll_start(uv_handle, UV_READABLE, nxt_uv_read_callback);
388         if (err < 0) {
389             napi_throw_error(obj->env_, NULL, "Failed to start uv.poll");
390             return NXT_UNIT_ERROR;
391         }
392 
393         port->data = uv_handle;
394         uv_handle->data = ctx;
395     }
396 
397     return nxt_unit_add_port(ctx, port);
398 }
399 
400 
401 void
402 Unit::remove_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id)
403 {
404     nxt_unit_port_t  *port;
405 
406     port = nxt_unit_find_port(ctx, port_id);
407     if (port == NULL) {
408         return;
409     }
410 
411     if (port->in_fd != -1 && port->data != NULL) {
412         uv_poll_stop((uv_poll_t *) port->data);
413 
414         delete (uv_poll_t *) port->data;
415     }
416 
417     nxt_unit_remove_port(ctx, port_id);
418 }
419 
420 
421 napi_value
422 Unit::get_server_object()
423 {
424     napi_value   unit_obj, server_obj;
425     napi_status  status;
426 
427     status = napi_get_reference_value(env_, wrapper_, &unit_obj);
428     if (status != napi_ok) {
429         return nullptr;
430     }
431 
432     status = napi_get_named_property(env_, unit_obj, "server", &server_obj);
433     if (status != napi_ok) {
434         return nullptr;
435     }
436 
437     return server_obj;
438 }
439 
440 
441 napi_status
442 Unit::create_headers(nxt_unit_request_info_t *req, napi_value request)
443 {
444     uint32_t            i;
445     const char          *p;
446     napi_value          headers, raw_headers, str;
447     napi_status         status;
448     nxt_unit_field_t    *f;
449     nxt_unit_request_t  *r;
450 
451     r = req->request;
452 
453     status = napi_create_object(env_, &headers);
454     if (status != napi_ok) {
455         return status;
456     }
457 
458     status = napi_create_array_with_length(env_, r->fields_count * 2,
459                                            &raw_headers);
460     if (status != napi_ok) {
461         return status;
462     }
463 
464     for (i = 0; i < r->fields_count; i++) {
465         f = r->fields + i;
466 
467         status = this->append_header(f, headers, raw_headers, i);
468         if (status != napi_ok) {
469             return status;
470         }
471     }
472 
473     status = napi_set_named_property(env_, request, "headers", headers);
474     if (status != napi_ok) {
475         return status;
476     }
477 
478     status = napi_set_named_property(env_, request, "rawHeaders", raw_headers);
479     if (status != napi_ok) {
480         return status;
481     }
482 
483     p = (const char *) nxt_unit_sptr_get(&r->version);
484 
485     status = napi_create_string_latin1(env_, p, r->version_length, &str);
486     if (status != napi_ok) {
487         return status;
488     }
489 
490     status = napi_set_named_property(env_, request, "httpVersion", str);
491     if (status != napi_ok) {
492         return status;
493     }
494 
495     p = (const char *) nxt_unit_sptr_get(&r->method);
496 
497     status = napi_create_string_latin1(env_, p, r->method_length, &str);
498     if (status != napi_ok) {
499         return status;
500     }
501 
502     status = napi_set_named_property(env_, request, "method", str);
503     if (status != napi_ok) {
504         return status;
505     }
506 
507     p = (const char *) nxt_unit_sptr_get(&r->target);
508 
509     status = napi_create_string_latin1(env_, p, r->target_length, &str);
510     if (status != napi_ok) {
511         return status;
512     }
513 
514     status = napi_set_named_property(env_, request, "url", str);
515     if (status != napi_ok) {
516         return status;
517     }
518 
519     return napi_ok;
520 }
521 
522 
523 inline napi_status
524 Unit::append_header(nxt_unit_field_t *f, napi_value headers,
525                     napi_value raw_headers, uint32_t idx)
526 {
527     const char   *name, *value;
528     napi_value   str, vstr;
529     napi_status  status;
530 
531     value = (const char *) nxt_unit_sptr_get(&f->value);
532 
533     status = napi_create_string_latin1(env_, value, f->value_length, &vstr);
534     if (status != napi_ok) {
535         return status;
536     }
537 
538     name = (const char *) nxt_unit_sptr_get(&f->name);
539 
540     status = napi_set_named_property(env_, headers, name, vstr);
541     if (status != napi_ok) {
542         return status;
543     }
544 
545     status = napi_create_string_latin1(env_, name, f->name_length, &str);
546     if (status != napi_ok) {
547         return status;
548     }
549 
550     status = napi_set_element(env_, raw_headers, idx * 2, str);
551     if (status != napi_ok) {
552         return status;
553     }
554 
555     status = napi_set_element(env_, raw_headers, idx * 2 + 1, vstr);
556     if (status != napi_ok) {
557         return status;
558     }
559 
560     return napi_ok;
561 }
562 
563 
564 napi_value
565 Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req)
566 {
567     napi_value   constructor, return_val, req_pointer;
568     napi_status  status;
569 
570     status = napi_get_named_property(env_, server_obj, "socket",
571                                      &constructor);
572     if (status != napi_ok) {
573         return nullptr;
574     }
575 
576     status = napi_new_instance(env_, constructor, 0, NULL, &return_val);
577     if (status != napi_ok) {
578         return nullptr;
579     }
580 
581     status = napi_create_int64(env_, (uintptr_t) req, &req_pointer);
582     if (status != napi_ok) {
583         return nullptr;
584     }
585 
586     status = napi_set_named_property(env_, return_val, "req_pointer",
587                                      req_pointer);
588     if (status != napi_ok) {
589         return nullptr;
590     }
591 
592     return return_val;
593 }
594 
595 
596 napi_value
597 Unit::create_request(napi_value server_obj, napi_value socket)
598 {
599     napi_value   constructor, return_val;
600     napi_status  status;
601 
602     status = napi_get_named_property(env_, server_obj, "request",
603                                      &constructor);
604     if (status != napi_ok) {
605         return nullptr;
606     }
607 
608     status = napi_new_instance(env_, constructor, 1, &server_obj,
609                                &return_val);
610     if (status != napi_ok) {
611         return nullptr;
612     }
613 
614     status = napi_set_named_property(env_, return_val, "socket", socket);
615     if (status != napi_ok) {
616         return nullptr;
617     }
618 
619     return return_val;
620 }
621 
622 
623 napi_value
624 Unit::create_response(napi_value server_obj, napi_value socket,
625                       napi_value request, nxt_unit_request_info_t *req,
626                       Unit *obj)
627 {
628     napi_value   constructor, return_val, req_num;
629     napi_status  status;
630 
631     status = napi_get_named_property(env_, server_obj, "response",
632                                      &constructor);
633     if (status != napi_ok) {
634         return nullptr;
635     }
636 
637     status = napi_new_instance(env_, constructor, 1, &request, &return_val);
638     if (status != napi_ok) {
639         return nullptr;
640     }
641 
642     status = napi_set_named_property(env_, return_val, "socket", socket);
643     if (status != napi_ok) {
644         return nullptr;
645     }
646 
647     status = napi_create_int64(env_, (int64_t) (uintptr_t) req, &req_num);
648     if (status != napi_ok) {
649         return nullptr;
650     }
651 
652     status = napi_set_named_property(env_, return_val, "_req_point", req_num);
653     if (status != napi_ok) {
654         return nullptr;
655     }
656 
657     return return_val;
658 }
659 
660 
661 napi_value
662 Unit::response_send_headers(napi_env env, napi_callback_info info)
663 {
664     int                      ret;
665     char                     *ptr, *name_ptr;
666     bool                     is_array;
667     size_t                   argc, name_len, value_len;
668     int64_t                  req_p;
669     uint32_t                 status_code, header_len, keys_len, array_len;
670     uint32_t                 keys_count, i, j;
671     uint16_t                 hash;
672     napi_value               this_arg, headers, keys, name, value, array_val;
673     napi_value               req_num;
674     napi_status              status;
675     nxt_unit_field_t         *f;
676     nxt_unit_request_info_t  *req;
677     napi_value               argv[5];
678 
679     argc = 5;
680 
681     status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL);
682     if (status != napi_ok) {
683         return nullptr;
684     }
685 
686     if (argc != 5) {
687         napi_throw_error(env, NULL, "Wrong args count. Need three: "
688                          "statusCode, headers, headers count, headers length");
689         return nullptr;
690     }
691 
692     status = napi_get_named_property(env, argv[0], "_req_point", &req_num);
693     if (status != napi_ok) {
694         napi_throw_error(env, NULL, "Failed to get request pointer");
695         return nullptr;
696     }
697 
698     status = napi_get_value_int64(env, req_num, &req_p);
699     if (status != napi_ok) {
700         napi_throw_error(env, NULL, "Failed to get request pointer");
701         return nullptr;
702     }
703 
704     req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
705 
706     status = napi_get_value_uint32(env, argv[1], &status_code);
707     if (status != napi_ok) {
708         goto failed;
709     }
710 
711     status = napi_get_value_uint32(env, argv[3], &keys_count);
712     if (status != napi_ok) {
713         goto failed;
714     }
715 
716     status = napi_get_value_uint32(env, argv[4], &header_len);
717     if (status != napi_ok) {
718         goto failed;
719     }
720 
721     /* Need to reserve extra byte for C-string 0-termination. */
722     header_len++;
723 
724     headers = argv[2];
725 
726     ret = nxt_unit_response_init(req, status_code, keys_count, header_len);
727     if (ret != NXT_UNIT_OK) {
728         goto failed;
729     }
730 
731     status = napi_get_property_names(env, headers, &keys);
732     if (status != napi_ok) {
733         goto failed;
734     }
735 
736     status = napi_get_array_length(env, keys, &keys_len);
737     if (status != napi_ok) {
738         goto failed;
739     }
740 
741     ptr = req->response_buf->free;
742 
743     for (i = 0; i < keys_len; i++) {
744         status = napi_get_element(env, keys, i, &name);
745         if (status != napi_ok) {
746             goto failed;
747         }
748 
749         status = napi_get_property(env, headers, name, &value);
750         if (status != napi_ok) {
751             goto failed;
752         }
753 
754         status = napi_get_value_string_latin1(env, name, ptr, header_len,
755                                               &name_len);
756         if (status != napi_ok) {
757             goto failed;
758         }
759 
760         name_ptr = ptr;
761 
762         ptr += name_len;
763         header_len -= name_len;
764 
765         hash = nxt_unit_field_hash(name_ptr, name_len);
766 
767         status = napi_is_array(env, value, &is_array);
768         if (status != napi_ok) {
769             goto failed;
770         }
771 
772         if (is_array) {
773             status = napi_get_array_length(env, value, &array_len);
774             if (status != napi_ok) {
775                 goto failed;
776             }
777 
778             for (j = 0; j < array_len; j++) {
779                 status = napi_get_element(env, value, j, &array_val);
780                 if (status != napi_ok) {
781                     goto failed;
782                 }
783 
784                 status = napi_get_value_string_latin1(env, array_val, ptr,
785                                                       header_len,
786                                                       &value_len);
787                 if (status != napi_ok) {
788                     goto failed;
789                 }
790 
791                 f = req->response->fields + req->response->fields_count;
792                 f->skip = 0;
793 
794                 nxt_unit_sptr_set(&f->name, name_ptr);
795 
796                 f->name_length = name_len;
797                 f->hash = hash;
798 
799                 nxt_unit_sptr_set(&f->value, ptr);
800                 f->value_length = (uint32_t) value_len;
801 
802                 ptr += value_len;
803                 header_len -= value_len;
804 
805                 req->response->fields_count++;
806             }
807 
808         } else {
809             status = napi_get_value_string_latin1(env, value, ptr, 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 
833     req->response_buf->free = ptr;
834 
835     ret = nxt_unit_response_send(req);
836     if (ret != NXT_UNIT_OK) {
837         goto failed;
838     }
839 
840     return this_arg;
841 
842 failed:
843 
844     req->response->fields_count = 0;
845 
846     napi_throw_error(env, NULL, "Failed to write headers");
847 
848     return nullptr;
849 }
850 
851 
852 napi_value
853 Unit::response_write(napi_env env, napi_callback_info info)
854 {
855     int                      ret;
856     char                     *ptr;
857     size_t                   argc, have_buf_len;
858     int64_t                  req_p;
859     uint32_t                 buf_len;
860     napi_value               this_arg, req_num;
861     napi_status              status;
862     nxt_unit_buf_t           *buf;
863     napi_valuetype           buf_type;
864     nxt_unit_request_info_t  *req;
865     napi_value               argv[3];
866 
867     argc = 3;
868 
869     status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL);
870     if (status != napi_ok) {
871         goto failed;
872     }
873 
874     if (argc != 3) {
875         napi_throw_error(env, NULL, "Wrong args count. Need two: "
876                          "chunk, chunk length");
877         return nullptr;
878     }
879 
880     status = napi_get_named_property(env, argv[0], "_req_point", &req_num);
881     if (status != napi_ok) {
882         napi_throw_error(env, NULL, "Failed to get request pointer");
883         return nullptr;
884     }
885 
886     status = napi_get_value_int64(env, req_num, &req_p);
887     if (status != napi_ok) {
888         napi_throw_error(env, NULL, "Failed to get request pointer");
889         return nullptr;
890     }
891 
892     req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
893 
894     status = napi_get_value_uint32(env, argv[2], &buf_len);
895     if (status != napi_ok) {
896         goto failed;
897     }
898 
899     status = napi_typeof(env, argv[1], &buf_type);
900     if (status != napi_ok) {
901         goto failed;
902     }
903 
904     buf_len++;
905 
906     buf = nxt_unit_response_buf_alloc(req, buf_len);
907     if (buf == NULL) {
908         goto failed;
909     }
910 
911     if (buf_type == napi_string) {
912         /* TODO: will work only for utf8 content-type */
913 
914         status = napi_get_value_string_utf8(env, argv[1], buf->free,
915                                             buf_len, &have_buf_len);
916 
917     } else {
918         status = napi_get_buffer_info(env, argv[1], (void **) &ptr,
919                                       &have_buf_len);
920 
921         memcpy(buf->free, ptr, have_buf_len);
922     }
923 
924     if (status != napi_ok) {
925         goto failed;
926     }
927 
928     buf->free += have_buf_len;
929 
930     ret = nxt_unit_buf_send(buf);
931     if (ret != NXT_UNIT_OK) {
932         goto failed;
933     }
934 
935     return this_arg;
936 
937 failed:
938 
939     napi_throw_error(env, NULL, "Failed to write body");
940 
941     return nullptr;
942 }
943 
944 
945 napi_value
946 Unit::response_end(napi_env env, napi_callback_info info)
947 {
948     size_t                   argc;
949     int64_t                  req_p;
950     napi_value               resp, this_arg, req_num;
951     napi_status              status;
952     nxt_unit_request_info_t  *req;
953 
954     argc = 1;
955 
956     status = napi_get_cb_info(env, info, &argc, &resp, &this_arg, NULL);
957     if (status != napi_ok) {
958         napi_throw_error(env, NULL, "Failed to finalize sending body");
959         return nullptr;
960     }
961 
962     status = napi_get_named_property(env, resp, "_req_point", &req_num);
963     if (status != napi_ok) {
964         napi_throw_error(env, NULL, "Failed to get request pointer");
965         return nullptr;
966     }
967 
968     status = napi_get_value_int64(env, req_num, &req_p);
969     if (status != napi_ok) {
970         napi_throw_error(env, NULL, "Failed to get request pointer");
971         return nullptr;
972     }
973 
974     req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
975 
976     nxt_unit_request_done(req, NXT_UNIT_OK);
977 
978     return this_arg;
979 }
980