xref: /unit/src/nodejs/unit-http/unit.cpp (revision 871)
1802Salexander.borisov@nginx.com 
2802Salexander.borisov@nginx.com /*
3802Salexander.borisov@nginx.com  * Copyright (C) NGINX, Inc.
4802Salexander.borisov@nginx.com  */
5802Salexander.borisov@nginx.com 
6802Salexander.borisov@nginx.com #include "unit.h"
7802Salexander.borisov@nginx.com 
8828Salexander.borisov@nginx.com #include <unistd.h>
9828Salexander.borisov@nginx.com #include <fcntl.h>
10828Salexander.borisov@nginx.com 
11828Salexander.borisov@nginx.com #include <uv.h>
12828Salexander.borisov@nginx.com 
13802Salexander.borisov@nginx.com 
14802Salexander.borisov@nginx.com napi_ref Unit::constructor_;
15802Salexander.borisov@nginx.com 
16802Salexander.borisov@nginx.com 
17842Salexander.borisov@nginx.com struct nxt_nodejs_ctx_t {
18842Salexander.borisov@nginx.com     nxt_unit_port_id_t  port_id;
19842Salexander.borisov@nginx.com     uv_poll_t           poll;
20842Salexander.borisov@nginx.com };
21842Salexander.borisov@nginx.com 
22842Salexander.borisov@nginx.com 
23802Salexander.borisov@nginx.com Unit::Unit(napi_env env):
24802Salexander.borisov@nginx.com     env_(env),
25802Salexander.borisov@nginx.com     wrapper_(nullptr),
26802Salexander.borisov@nginx.com     unit_ctx_(nullptr)
27802Salexander.borisov@nginx.com {
28802Salexander.borisov@nginx.com }
29802Salexander.borisov@nginx.com 
30802Salexander.borisov@nginx.com 
31802Salexander.borisov@nginx.com Unit::~Unit()
32802Salexander.borisov@nginx.com {
33802Salexander.borisov@nginx.com     napi_delete_reference(env_, wrapper_);
34802Salexander.borisov@nginx.com }
35802Salexander.borisov@nginx.com 
36802Salexander.borisov@nginx.com 
37802Salexander.borisov@nginx.com napi_value
38802Salexander.borisov@nginx.com Unit::init(napi_env env, napi_value exports)
39802Salexander.borisov@nginx.com {
40802Salexander.borisov@nginx.com     napi_value   cons, fn;
41802Salexander.borisov@nginx.com     napi_status  status;
42802Salexander.borisov@nginx.com 
43802Salexander.borisov@nginx.com     napi_property_descriptor  properties[] = {
44802Salexander.borisov@nginx.com         { "createServer", 0, create_server, 0, 0, 0, napi_default, 0 },
45828Salexander.borisov@nginx.com         { "listen", 0, listen, 0, 0, 0, napi_default, 0 },
46828Salexander.borisov@nginx.com         { "_read", 0, _read, 0, 0, 0, napi_default, 0 }
47802Salexander.borisov@nginx.com     };
48802Salexander.borisov@nginx.com 
49802Salexander.borisov@nginx.com     status = napi_define_class(env, "Unit", NAPI_AUTO_LENGTH, create, nullptr,
50828Salexander.borisov@nginx.com                                3, properties, &cons);
51802Salexander.borisov@nginx.com     if (status != napi_ok) {
52802Salexander.borisov@nginx.com         goto failed;
53802Salexander.borisov@nginx.com     }
54802Salexander.borisov@nginx.com 
55802Salexander.borisov@nginx.com     status = napi_create_reference(env, cons, 1, &constructor_);
56802Salexander.borisov@nginx.com     if (status != napi_ok) {
57802Salexander.borisov@nginx.com         goto failed;
58802Salexander.borisov@nginx.com     }
59802Salexander.borisov@nginx.com 
60802Salexander.borisov@nginx.com     status = napi_set_named_property(env, exports, "Unit", cons);
61802Salexander.borisov@nginx.com     if (status != napi_ok) {
62802Salexander.borisov@nginx.com         goto failed;
63802Salexander.borisov@nginx.com     }
64802Salexander.borisov@nginx.com 
65802Salexander.borisov@nginx.com     status = napi_create_function(env, NULL, 0, response_send_headers, NULL,
66802Salexander.borisov@nginx.com                                   &fn);
67802Salexander.borisov@nginx.com     if (status != napi_ok) {
68802Salexander.borisov@nginx.com         goto failed;
69802Salexander.borisov@nginx.com     }
70802Salexander.borisov@nginx.com 
71802Salexander.borisov@nginx.com     status = napi_set_named_property(env, exports,
72802Salexander.borisov@nginx.com                                      "unit_response_headers", fn);
73802Salexander.borisov@nginx.com     if (status != napi_ok) {
74802Salexander.borisov@nginx.com         goto failed;
75802Salexander.borisov@nginx.com     }
76802Salexander.borisov@nginx.com 
77802Salexander.borisov@nginx.com     status = napi_create_function(env, NULL, 0, response_write, NULL, &fn);
78802Salexander.borisov@nginx.com     if (status != napi_ok) {
79802Salexander.borisov@nginx.com         goto failed;
80802Salexander.borisov@nginx.com     }
81802Salexander.borisov@nginx.com 
82802Salexander.borisov@nginx.com     status = napi_set_named_property(env, exports, "unit_response_write", fn);
83802Salexander.borisov@nginx.com     if (status != napi_ok) {
84802Salexander.borisov@nginx.com         goto failed;
85802Salexander.borisov@nginx.com     }
86802Salexander.borisov@nginx.com 
87802Salexander.borisov@nginx.com     status = napi_create_function(env, NULL, 0, response_end, NULL, &fn);
88802Salexander.borisov@nginx.com     if (status != napi_ok) {
89802Salexander.borisov@nginx.com         goto failed;
90802Salexander.borisov@nginx.com     }
91802Salexander.borisov@nginx.com 
92802Salexander.borisov@nginx.com     status = napi_set_named_property(env, exports, "unit_response_end", fn);
93802Salexander.borisov@nginx.com     if (status != napi_ok) {
94802Salexander.borisov@nginx.com         goto failed;
95802Salexander.borisov@nginx.com     }
96802Salexander.borisov@nginx.com 
97802Salexander.borisov@nginx.com     return exports;
98802Salexander.borisov@nginx.com 
99802Salexander.borisov@nginx.com failed:
100802Salexander.borisov@nginx.com 
101802Salexander.borisov@nginx.com     napi_throw_error(env, NULL, "Failed to define Unit class");
102802Salexander.borisov@nginx.com 
103802Salexander.borisov@nginx.com     return nullptr;
104802Salexander.borisov@nginx.com }
105802Salexander.borisov@nginx.com 
106802Salexander.borisov@nginx.com 
107802Salexander.borisov@nginx.com void
108802Salexander.borisov@nginx.com Unit::destroy(napi_env env, void *nativeObject, void *finalize_hint)
109802Salexander.borisov@nginx.com {
110802Salexander.borisov@nginx.com     Unit  *obj = reinterpret_cast<Unit *>(nativeObject);
111802Salexander.borisov@nginx.com 
112802Salexander.borisov@nginx.com     delete obj;
113802Salexander.borisov@nginx.com }
114802Salexander.borisov@nginx.com 
115802Salexander.borisov@nginx.com 
116802Salexander.borisov@nginx.com napi_value
117802Salexander.borisov@nginx.com Unit::create(napi_env env, napi_callback_info info)
118802Salexander.borisov@nginx.com {
119802Salexander.borisov@nginx.com     Unit         *obj;
120841Salexander.borisov@nginx.com     napi_ref     ref;
121802Salexander.borisov@nginx.com     napi_value   target, cons, instance, jsthis;
122802Salexander.borisov@nginx.com     napi_status  status;
123802Salexander.borisov@nginx.com 
124802Salexander.borisov@nginx.com     status = napi_get_new_target(env, info, &target);
125802Salexander.borisov@nginx.com     if (status != napi_ok) {
126802Salexander.borisov@nginx.com         goto failed;
127802Salexander.borisov@nginx.com     }
128802Salexander.borisov@nginx.com 
129802Salexander.borisov@nginx.com     if (target != nullptr) {
130802Salexander.borisov@nginx.com         /* Invoked as constructor: `new Unit(...)` */
131802Salexander.borisov@nginx.com         status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis,
132802Salexander.borisov@nginx.com                                   nullptr);
133802Salexander.borisov@nginx.com         if (status != napi_ok) {
134802Salexander.borisov@nginx.com             goto failed;
135802Salexander.borisov@nginx.com         }
136802Salexander.borisov@nginx.com 
137802Salexander.borisov@nginx.com         obj = new Unit(env);
138802Salexander.borisov@nginx.com 
139802Salexander.borisov@nginx.com         status = napi_wrap(env, jsthis, reinterpret_cast<void *>(obj),
140802Salexander.borisov@nginx.com                            destroy, nullptr, &obj->wrapper_);
141802Salexander.borisov@nginx.com         if (status != napi_ok) {
142802Salexander.borisov@nginx.com             goto failed;
143802Salexander.borisov@nginx.com         }
144802Salexander.borisov@nginx.com 
145841Salexander.borisov@nginx.com         status = napi_create_reference(env, jsthis, 1, &ref);
146841Salexander.borisov@nginx.com         if (status != napi_ok) {
147841Salexander.borisov@nginx.com             goto failed;
148841Salexander.borisov@nginx.com         }
149841Salexander.borisov@nginx.com 
150802Salexander.borisov@nginx.com         return jsthis;
151802Salexander.borisov@nginx.com     }
152802Salexander.borisov@nginx.com 
153802Salexander.borisov@nginx.com     /* Invoked as plain function `Unit(...)`, turn into construct call. */
154802Salexander.borisov@nginx.com     status = napi_get_reference_value(env, constructor_, &cons);
155802Salexander.borisov@nginx.com     if (status != napi_ok) {
156802Salexander.borisov@nginx.com         goto failed;
157802Salexander.borisov@nginx.com     }
158802Salexander.borisov@nginx.com 
159802Salexander.borisov@nginx.com     status = napi_new_instance(env, cons, 0, nullptr, &instance);
160802Salexander.borisov@nginx.com     if (status != napi_ok) {
161802Salexander.borisov@nginx.com         goto failed;
162802Salexander.borisov@nginx.com     }
163802Salexander.borisov@nginx.com 
164841Salexander.borisov@nginx.com     status = napi_create_reference(env, instance, 1, &ref);
165841Salexander.borisov@nginx.com     if (status != napi_ok) {
166841Salexander.borisov@nginx.com         goto failed;
167841Salexander.borisov@nginx.com     }
168841Salexander.borisov@nginx.com 
169802Salexander.borisov@nginx.com     return instance;
170802Salexander.borisov@nginx.com 
171802Salexander.borisov@nginx.com failed:
172802Salexander.borisov@nginx.com 
173802Salexander.borisov@nginx.com     napi_throw_error(env, NULL, "Failed to create Unit object");
174802Salexander.borisov@nginx.com 
175802Salexander.borisov@nginx.com     return nullptr;
176802Salexander.borisov@nginx.com }
177802Salexander.borisov@nginx.com 
178802Salexander.borisov@nginx.com 
179802Salexander.borisov@nginx.com napi_value
180802Salexander.borisov@nginx.com Unit::create_server(napi_env env, napi_callback_info info)
181802Salexander.borisov@nginx.com {
182802Salexander.borisov@nginx.com     Unit             *obj;
183802Salexander.borisov@nginx.com     size_t           argc;
184828Salexander.borisov@nginx.com     napi_value       jsthis, argv;
185802Salexander.borisov@nginx.com     napi_status      status;
186802Salexander.borisov@nginx.com     nxt_unit_init_t  unit_init;
187802Salexander.borisov@nginx.com 
188802Salexander.borisov@nginx.com     argc = 1;
189802Salexander.borisov@nginx.com 
190828Salexander.borisov@nginx.com     status = napi_get_cb_info(env, info, &argc, &argv, &jsthis, nullptr);
191802Salexander.borisov@nginx.com     if (status != napi_ok) {
192802Salexander.borisov@nginx.com         goto failed;
193802Salexander.borisov@nginx.com     }
194802Salexander.borisov@nginx.com 
195802Salexander.borisov@nginx.com     status = napi_unwrap(env, jsthis, reinterpret_cast<void **>(&obj));
196802Salexander.borisov@nginx.com     if (status != napi_ok) {
197802Salexander.borisov@nginx.com         goto failed;
198802Salexander.borisov@nginx.com     }
199802Salexander.borisov@nginx.com 
200802Salexander.borisov@nginx.com     memset(&unit_init, 0, sizeof(nxt_unit_init_t));
201802Salexander.borisov@nginx.com 
202802Salexander.borisov@nginx.com     unit_init.data = obj;
203802Salexander.borisov@nginx.com     unit_init.callbacks.request_handler = request_handler;
204828Salexander.borisov@nginx.com     unit_init.callbacks.add_port        = add_port;
205828Salexander.borisov@nginx.com     unit_init.callbacks.remove_port     = remove_port;
206842Salexander.borisov@nginx.com     unit_init.callbacks.quit            = quit;
207802Salexander.borisov@nginx.com 
208802Salexander.borisov@nginx.com     obj->unit_ctx_ = nxt_unit_init(&unit_init);
209802Salexander.borisov@nginx.com     if (obj->unit_ctx_ == NULL) {
210802Salexander.borisov@nginx.com         goto failed;
211802Salexander.borisov@nginx.com     }
212802Salexander.borisov@nginx.com 
213802Salexander.borisov@nginx.com     return nullptr;
214802Salexander.borisov@nginx.com 
215802Salexander.borisov@nginx.com failed:
216802Salexander.borisov@nginx.com 
217802Salexander.borisov@nginx.com     napi_throw_error(env, NULL, "Failed to create Unit object");
218802Salexander.borisov@nginx.com 
219802Salexander.borisov@nginx.com     return nullptr;
220802Salexander.borisov@nginx.com }
221802Salexander.borisov@nginx.com 
222802Salexander.borisov@nginx.com 
223802Salexander.borisov@nginx.com napi_value
224802Salexander.borisov@nginx.com Unit::listen(napi_env env, napi_callback_info info)
225802Salexander.borisov@nginx.com {
226828Salexander.borisov@nginx.com     return nullptr;
227828Salexander.borisov@nginx.com }
228828Salexander.borisov@nginx.com 
229802Salexander.borisov@nginx.com 
230828Salexander.borisov@nginx.com napi_value
231828Salexander.borisov@nginx.com Unit::_read(napi_env env, napi_callback_info info)
232828Salexander.borisov@nginx.com {
233828Salexander.borisov@nginx.com     Unit                     *obj;
234828Salexander.borisov@nginx.com     void                     *data;
235828Salexander.borisov@nginx.com     size_t                   argc;
236828Salexander.borisov@nginx.com     int64_t                  req_pointer;
237828Salexander.borisov@nginx.com     napi_value               jsthis, buffer, argv;
238828Salexander.borisov@nginx.com     napi_status              status;
239828Salexander.borisov@nginx.com     nxt_unit_request_info_t  *req;
240828Salexander.borisov@nginx.com 
241828Salexander.borisov@nginx.com     argc = 1;
242828Salexander.borisov@nginx.com 
243828Salexander.borisov@nginx.com     status = napi_get_cb_info(env, info, &argc, &argv, &jsthis, nullptr);
244802Salexander.borisov@nginx.com     if (status != napi_ok) {
245828Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Failed to get arguments from js");
246828Salexander.borisov@nginx.com         return nullptr;
247802Salexander.borisov@nginx.com     }
248802Salexander.borisov@nginx.com 
249802Salexander.borisov@nginx.com     status = napi_unwrap(env, jsthis, reinterpret_cast<void **>(&obj));
250802Salexander.borisov@nginx.com     if (status != napi_ok) {
251828Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Failed to get Unit object form js");
252828Salexander.borisov@nginx.com         return nullptr;
253802Salexander.borisov@nginx.com     }
254802Salexander.borisov@nginx.com 
255828Salexander.borisov@nginx.com     status = napi_get_value_int64(env, argv, &req_pointer);
256828Salexander.borisov@nginx.com     if (status != napi_ok) {
257828Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Failed to get request pointer");
258802Salexander.borisov@nginx.com         return nullptr;
259802Salexander.borisov@nginx.com     }
260802Salexander.borisov@nginx.com 
261828Salexander.borisov@nginx.com     req = (nxt_unit_request_info_t *) (uintptr_t) req_pointer;
262828Salexander.borisov@nginx.com 
263828Salexander.borisov@nginx.com     status = napi_create_buffer(env, (size_t) req->content_length,
264828Salexander.borisov@nginx.com                                 &data, &buffer);
265828Salexander.borisov@nginx.com     if (status != napi_ok) {
266828Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Failed to create request buffer");
267802Salexander.borisov@nginx.com         return nullptr;
268802Salexander.borisov@nginx.com     }
269802Salexander.borisov@nginx.com 
270828Salexander.borisov@nginx.com     nxt_unit_request_read(req, data, req->content_length);
271802Salexander.borisov@nginx.com 
272828Salexander.borisov@nginx.com     return buffer;
273802Salexander.borisov@nginx.com }
274802Salexander.borisov@nginx.com 
275802Salexander.borisov@nginx.com 
276802Salexander.borisov@nginx.com void
277802Salexander.borisov@nginx.com Unit::request_handler(nxt_unit_request_info_t *req)
278802Salexander.borisov@nginx.com {
279*871Salexander.borisov@nginx.com     Unit                 *obj;
280*871Salexander.borisov@nginx.com     napi_value           socket, request, response, global, server_obj;
281*871Salexander.borisov@nginx.com     napi_value           emit_events, events_res, async_name, resource_object;
282*871Salexander.borisov@nginx.com     napi_status          status;
283*871Salexander.borisov@nginx.com     napi_async_context   async_context;
284*871Salexander.borisov@nginx.com     napi_callback_scope  async_scope;
285*871Salexander.borisov@nginx.com     napi_value           events_args[3];
286802Salexander.borisov@nginx.com 
287802Salexander.borisov@nginx.com     obj = reinterpret_cast<Unit *>(req->unit->data);
288802Salexander.borisov@nginx.com 
289802Salexander.borisov@nginx.com     napi_handle_scope scope;
290802Salexander.borisov@nginx.com     status = napi_open_handle_scope(obj->env_, &scope);
291802Salexander.borisov@nginx.com     if (status != napi_ok) {
292802Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to create handle scope");
293802Salexander.borisov@nginx.com         return;
294802Salexander.borisov@nginx.com     }
295802Salexander.borisov@nginx.com 
296802Salexander.borisov@nginx.com     server_obj = obj->get_server_object();
297802Salexander.borisov@nginx.com     if (server_obj == nullptr) {
298802Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to get server object");
299802Salexander.borisov@nginx.com         return;
300802Salexander.borisov@nginx.com     }
301802Salexander.borisov@nginx.com 
302802Salexander.borisov@nginx.com     status = napi_get_global(obj->env_, &global);
303802Salexander.borisov@nginx.com     if (status != napi_ok) {
304802Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to get global variable");
305802Salexander.borisov@nginx.com         return;
306802Salexander.borisov@nginx.com     }
307802Salexander.borisov@nginx.com 
308802Salexander.borisov@nginx.com     socket = obj->create_socket(server_obj, req);
309802Salexander.borisov@nginx.com     if (socket == nullptr) {
310802Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to create socket object");
311802Salexander.borisov@nginx.com         return;
312802Salexander.borisov@nginx.com     }
313802Salexander.borisov@nginx.com 
314802Salexander.borisov@nginx.com     request = obj->create_request(server_obj, socket);
315802Salexander.borisov@nginx.com     if (request == nullptr) {
316802Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to create request object");
317802Salexander.borisov@nginx.com         return;
318802Salexander.borisov@nginx.com     }
319802Salexander.borisov@nginx.com 
320802Salexander.borisov@nginx.com     response = obj->create_response(server_obj, socket, request, req, obj);
321802Salexander.borisov@nginx.com     if (response == nullptr) {
322802Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to create response object");
323802Salexander.borisov@nginx.com         return;
324802Salexander.borisov@nginx.com     }
325802Salexander.borisov@nginx.com 
326802Salexander.borisov@nginx.com     status = obj->create_headers(req, request);
327802Salexander.borisov@nginx.com     if (status != napi_ok) {
328802Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to create headers");
329802Salexander.borisov@nginx.com         return;
330802Salexander.borisov@nginx.com     }
331802Salexander.borisov@nginx.com 
332*871Salexander.borisov@nginx.com     status = napi_get_named_property(obj->env_, server_obj, "emit_events",
333*871Salexander.borisov@nginx.com                                      &emit_events);
334828Salexander.borisov@nginx.com     if (status != napi_ok) {
335*871Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to get "
336*871Salexander.borisov@nginx.com                          "'emit_events' function");
337828Salexander.borisov@nginx.com         return;
338828Salexander.borisov@nginx.com     }
339828Salexander.borisov@nginx.com 
340828Salexander.borisov@nginx.com     events_args[0] = server_obj;
341828Salexander.borisov@nginx.com     events_args[1] = request;
342828Salexander.borisov@nginx.com     events_args[2] = response;
343828Salexander.borisov@nginx.com 
344*871Salexander.borisov@nginx.com     status = napi_create_string_utf8(obj->env_, "unit_request_handler",
345*871Salexander.borisov@nginx.com                                      sizeof("unit_request_handler") - 1,
346*871Salexander.borisov@nginx.com                                      &async_name);
347828Salexander.borisov@nginx.com     if (status != napi_ok) {
348*871Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to create utf-8 string");
349*871Salexander.borisov@nginx.com         return;
350*871Salexander.borisov@nginx.com     }
351*871Salexander.borisov@nginx.com 
352*871Salexander.borisov@nginx.com     status = napi_async_init(obj->env_, NULL, async_name, &async_context);
353*871Salexander.borisov@nginx.com     if (status != napi_ok) {
354*871Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to init async object");
355*871Salexander.borisov@nginx.com         return;
356*871Salexander.borisov@nginx.com     }
357*871Salexander.borisov@nginx.com 
358*871Salexander.borisov@nginx.com     status = napi_create_object(obj->env_, &resource_object);
359*871Salexander.borisov@nginx.com     if (status != napi_ok) {
360*871Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to create object for "
361*871Salexander.borisov@nginx.com                          "callback scope");
362828Salexander.borisov@nginx.com         return;
363828Salexander.borisov@nginx.com     }
364802Salexander.borisov@nginx.com 
365*871Salexander.borisov@nginx.com     status = napi_open_callback_scope(obj->env_, resource_object, async_context,
366*871Salexander.borisov@nginx.com                                       &async_scope);
367*871Salexander.borisov@nginx.com     if (status != napi_ok) {
368*871Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to open callback scope");
369*871Salexander.borisov@nginx.com         return;
370*871Salexander.borisov@nginx.com     }
371*871Salexander.borisov@nginx.com 
372*871Salexander.borisov@nginx.com     status = napi_make_callback(obj->env_, async_context, server_obj,
373*871Salexander.borisov@nginx.com                                 emit_events, 3, events_args, &events_res);
374*871Salexander.borisov@nginx.com     if (status != napi_ok) {
375*871Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to make callback");
376*871Salexander.borisov@nginx.com         return;
377*871Salexander.borisov@nginx.com     }
378*871Salexander.borisov@nginx.com 
379*871Salexander.borisov@nginx.com     status = napi_close_callback_scope(obj->env_, async_scope);
380*871Salexander.borisov@nginx.com     if (status != napi_ok) {
381*871Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to close callback scope");
382*871Salexander.borisov@nginx.com         return;
383*871Salexander.borisov@nginx.com     }
384*871Salexander.borisov@nginx.com 
385*871Salexander.borisov@nginx.com     status = napi_async_destroy(obj->env_, async_context);
386*871Salexander.borisov@nginx.com     if (status != napi_ok) {
387*871Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to destroy async object");
388*871Salexander.borisov@nginx.com         return;
389*871Salexander.borisov@nginx.com     }
390*871Salexander.borisov@nginx.com 
391*871Salexander.borisov@nginx.com     status = napi_close_handle_scope(obj->env_, scope);
392*871Salexander.borisov@nginx.com     if (status != napi_ok) {
393*871Salexander.borisov@nginx.com         napi_throw_error(obj->env_, NULL, "Failed to close handle scope");
394*871Salexander.borisov@nginx.com     }
395802Salexander.borisov@nginx.com }
396802Salexander.borisov@nginx.com 
397802Salexander.borisov@nginx.com 
398828Salexander.borisov@nginx.com void
399828Salexander.borisov@nginx.com nxt_uv_read_callback(uv_poll_t *handle, int status, int events)
400828Salexander.borisov@nginx.com {
401828Salexander.borisov@nginx.com     nxt_unit_run_once((nxt_unit_ctx_t *) handle->data);
402828Salexander.borisov@nginx.com }
403828Salexander.borisov@nginx.com 
404828Salexander.borisov@nginx.com 
405828Salexander.borisov@nginx.com int
406828Salexander.borisov@nginx.com Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
407828Salexander.borisov@nginx.com {
408842Salexander.borisov@nginx.com     int               err;
409842Salexander.borisov@nginx.com     Unit              *obj;
410842Salexander.borisov@nginx.com     uv_loop_t         *loop;
411842Salexander.borisov@nginx.com     napi_status       status;
412842Salexander.borisov@nginx.com     nxt_nodejs_ctx_t  *node_ctx;
413828Salexander.borisov@nginx.com 
414828Salexander.borisov@nginx.com     if (port->in_fd != -1) {
415828Salexander.borisov@nginx.com         obj = reinterpret_cast<Unit *>(ctx->unit->data);
416828Salexander.borisov@nginx.com 
417828Salexander.borisov@nginx.com         if (fcntl(port->in_fd, F_SETFL, O_NONBLOCK) == -1) {
418828Salexander.borisov@nginx.com             napi_throw_error(obj->env_, NULL, "Failed to upgrade read"
419828Salexander.borisov@nginx.com                              " file descriptor to O_NONBLOCK");
420828Salexander.borisov@nginx.com             return -1;
421828Salexander.borisov@nginx.com         }
422828Salexander.borisov@nginx.com 
423828Salexander.borisov@nginx.com         status = napi_get_uv_event_loop(obj->env_, &loop);
424828Salexander.borisov@nginx.com         if (status != napi_ok) {
425828Salexander.borisov@nginx.com             napi_throw_error(obj->env_, NULL, "Failed to get uv.loop");
426828Salexander.borisov@nginx.com             return NXT_UNIT_ERROR;
427828Salexander.borisov@nginx.com         }
428828Salexander.borisov@nginx.com 
429842Salexander.borisov@nginx.com         node_ctx = new nxt_nodejs_ctx_t;
430828Salexander.borisov@nginx.com 
431842Salexander.borisov@nginx.com         err = uv_poll_init(loop, &node_ctx->poll, port->in_fd);
432828Salexander.borisov@nginx.com         if (err < 0) {
433828Salexander.borisov@nginx.com             napi_throw_error(obj->env_, NULL, "Failed to init uv.poll");
434828Salexander.borisov@nginx.com             return NXT_UNIT_ERROR;
435828Salexander.borisov@nginx.com         }
436828Salexander.borisov@nginx.com 
437842Salexander.borisov@nginx.com         err = uv_poll_start(&node_ctx->poll, UV_READABLE, nxt_uv_read_callback);
438828Salexander.borisov@nginx.com         if (err < 0) {
439828Salexander.borisov@nginx.com             napi_throw_error(obj->env_, NULL, "Failed to start uv.poll");
440828Salexander.borisov@nginx.com             return NXT_UNIT_ERROR;
441828Salexander.borisov@nginx.com         }
442828Salexander.borisov@nginx.com 
443842Salexander.borisov@nginx.com         ctx->data = node_ctx;
444842Salexander.borisov@nginx.com 
445842Salexander.borisov@nginx.com         node_ctx->port_id = port->id;
446842Salexander.borisov@nginx.com         node_ctx->poll.data = ctx;
447828Salexander.borisov@nginx.com     }
448828Salexander.borisov@nginx.com 
449828Salexander.borisov@nginx.com     return nxt_unit_add_port(ctx, port);
450828Salexander.borisov@nginx.com }
451828Salexander.borisov@nginx.com 
452828Salexander.borisov@nginx.com 
453842Salexander.borisov@nginx.com inline bool
454842Salexander.borisov@nginx.com operator == (const nxt_unit_port_id_t &p1, const nxt_unit_port_id_t &p2)
455842Salexander.borisov@nginx.com {
456842Salexander.borisov@nginx.com     return p1.pid == p2.pid && p1.id == p2.id;
457842Salexander.borisov@nginx.com }
458842Salexander.borisov@nginx.com 
459842Salexander.borisov@nginx.com 
460828Salexander.borisov@nginx.com void
461828Salexander.borisov@nginx.com Unit::remove_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id)
462828Salexander.borisov@nginx.com {
463842Salexander.borisov@nginx.com     nxt_nodejs_ctx_t  *node_ctx;
464842Salexander.borisov@nginx.com 
465842Salexander.borisov@nginx.com     if (ctx->data != NULL) {
466842Salexander.borisov@nginx.com         node_ctx = (nxt_nodejs_ctx_t *) ctx->data;
467828Salexander.borisov@nginx.com 
468842Salexander.borisov@nginx.com         if (node_ctx->port_id == *port_id) {
469842Salexander.borisov@nginx.com             uv_poll_stop(&node_ctx->poll);
470828Salexander.borisov@nginx.com 
471842Salexander.borisov@nginx.com             delete node_ctx;
472828Salexander.borisov@nginx.com 
473842Salexander.borisov@nginx.com             ctx->data = NULL;
474842Salexander.borisov@nginx.com         }
475828Salexander.borisov@nginx.com     }
476828Salexander.borisov@nginx.com 
477828Salexander.borisov@nginx.com     nxt_unit_remove_port(ctx, port_id);
478828Salexander.borisov@nginx.com }
479828Salexander.borisov@nginx.com 
480828Salexander.borisov@nginx.com 
481842Salexander.borisov@nginx.com void
482842Salexander.borisov@nginx.com Unit::quit(nxt_unit_ctx_t *ctx)
483842Salexander.borisov@nginx.com {
484842Salexander.borisov@nginx.com     nxt_unit_done(ctx);
485842Salexander.borisov@nginx.com }
486842Salexander.borisov@nginx.com 
487842Salexander.borisov@nginx.com 
488802Salexander.borisov@nginx.com napi_value
489802Salexander.borisov@nginx.com Unit::get_server_object()
490802Salexander.borisov@nginx.com {
491802Salexander.borisov@nginx.com     napi_value   unit_obj, server_obj;
492802Salexander.borisov@nginx.com     napi_status  status;
493802Salexander.borisov@nginx.com 
494802Salexander.borisov@nginx.com     status = napi_get_reference_value(env_, wrapper_, &unit_obj);
495802Salexander.borisov@nginx.com     if (status != napi_ok) {
496802Salexander.borisov@nginx.com         return nullptr;
497802Salexander.borisov@nginx.com     }
498802Salexander.borisov@nginx.com 
499802Salexander.borisov@nginx.com     status = napi_get_named_property(env_, unit_obj, "server", &server_obj);
500802Salexander.borisov@nginx.com     if (status != napi_ok) {
501802Salexander.borisov@nginx.com         return nullptr;
502802Salexander.borisov@nginx.com     }
503802Salexander.borisov@nginx.com 
504802Salexander.borisov@nginx.com     return server_obj;
505802Salexander.borisov@nginx.com }
506802Salexander.borisov@nginx.com 
507802Salexander.borisov@nginx.com 
508802Salexander.borisov@nginx.com napi_status
509802Salexander.borisov@nginx.com Unit::create_headers(nxt_unit_request_info_t *req, napi_value request)
510802Salexander.borisov@nginx.com {
511802Salexander.borisov@nginx.com     uint32_t            i;
512802Salexander.borisov@nginx.com     const char          *p;
513802Salexander.borisov@nginx.com     napi_value          headers, raw_headers, str;
514802Salexander.borisov@nginx.com     napi_status         status;
515802Salexander.borisov@nginx.com     nxt_unit_field_t    *f;
516802Salexander.borisov@nginx.com     nxt_unit_request_t  *r;
517802Salexander.borisov@nginx.com 
518802Salexander.borisov@nginx.com     r = req->request;
519802Salexander.borisov@nginx.com 
520802Salexander.borisov@nginx.com     status = napi_create_object(env_, &headers);
521802Salexander.borisov@nginx.com     if (status != napi_ok) {
522802Salexander.borisov@nginx.com         return status;
523802Salexander.borisov@nginx.com     }
524802Salexander.borisov@nginx.com 
525802Salexander.borisov@nginx.com     status = napi_create_array_with_length(env_, r->fields_count * 2,
526802Salexander.borisov@nginx.com                                            &raw_headers);
527802Salexander.borisov@nginx.com     if (status != napi_ok) {
528802Salexander.borisov@nginx.com         return status;
529802Salexander.borisov@nginx.com     }
530802Salexander.borisov@nginx.com 
531802Salexander.borisov@nginx.com     for (i = 0; i < r->fields_count; i++) {
532802Salexander.borisov@nginx.com         f = r->fields + i;
533802Salexander.borisov@nginx.com 
534802Salexander.borisov@nginx.com         status = this->append_header(f, headers, raw_headers, i);
535802Salexander.borisov@nginx.com         if (status != napi_ok) {
536802Salexander.borisov@nginx.com             return status;
537802Salexander.borisov@nginx.com         }
538802Salexander.borisov@nginx.com     }
539802Salexander.borisov@nginx.com 
540802Salexander.borisov@nginx.com     status = napi_set_named_property(env_, request, "headers", headers);
541802Salexander.borisov@nginx.com     if (status != napi_ok) {
542802Salexander.borisov@nginx.com         return status;
543802Salexander.borisov@nginx.com     }
544802Salexander.borisov@nginx.com 
545830Szelenkov@nginx.com     status = napi_set_named_property(env_, request, "rawHeaders", raw_headers);
546802Salexander.borisov@nginx.com     if (status != napi_ok) {
547802Salexander.borisov@nginx.com         return status;
548802Salexander.borisov@nginx.com     }
549802Salexander.borisov@nginx.com 
550802Salexander.borisov@nginx.com     p = (const char *) nxt_unit_sptr_get(&r->version);
551802Salexander.borisov@nginx.com 
552802Salexander.borisov@nginx.com     status = napi_create_string_latin1(env_, p, r->version_length, &str);
553802Salexander.borisov@nginx.com     if (status != napi_ok) {
554802Salexander.borisov@nginx.com         return status;
555802Salexander.borisov@nginx.com     }
556802Salexander.borisov@nginx.com 
557802Salexander.borisov@nginx.com     status = napi_set_named_property(env_, request, "httpVersion", str);
558802Salexander.borisov@nginx.com     if (status != napi_ok) {
559802Salexander.borisov@nginx.com         return status;
560802Salexander.borisov@nginx.com     }
561802Salexander.borisov@nginx.com 
562802Salexander.borisov@nginx.com     p = (const char *) nxt_unit_sptr_get(&r->method);
563802Salexander.borisov@nginx.com 
564802Salexander.borisov@nginx.com     status = napi_create_string_latin1(env_, p, r->method_length, &str);
565802Salexander.borisov@nginx.com     if (status != napi_ok) {
566802Salexander.borisov@nginx.com         return status;
567802Salexander.borisov@nginx.com     }
568802Salexander.borisov@nginx.com 
569802Salexander.borisov@nginx.com     status = napi_set_named_property(env_, request, "method", str);
570802Salexander.borisov@nginx.com     if (status != napi_ok) {
571802Salexander.borisov@nginx.com         return status;
572802Salexander.borisov@nginx.com     }
573802Salexander.borisov@nginx.com 
574802Salexander.borisov@nginx.com     p = (const char *) nxt_unit_sptr_get(&r->target);
575802Salexander.borisov@nginx.com 
576802Salexander.borisov@nginx.com     status = napi_create_string_latin1(env_, p, r->target_length, &str);
577802Salexander.borisov@nginx.com     if (status != napi_ok) {
578802Salexander.borisov@nginx.com         return status;
579802Salexander.borisov@nginx.com     }
580802Salexander.borisov@nginx.com 
581802Salexander.borisov@nginx.com     status = napi_set_named_property(env_, request, "url", str);
582802Salexander.borisov@nginx.com     if (status != napi_ok) {
583802Salexander.borisov@nginx.com         return status;
584802Salexander.borisov@nginx.com     }
585802Salexander.borisov@nginx.com 
586802Salexander.borisov@nginx.com     return napi_ok;
587802Salexander.borisov@nginx.com }
588802Salexander.borisov@nginx.com 
589802Salexander.borisov@nginx.com 
590802Salexander.borisov@nginx.com inline napi_status
591802Salexander.borisov@nginx.com Unit::append_header(nxt_unit_field_t *f, napi_value headers,
592802Salexander.borisov@nginx.com                     napi_value raw_headers, uint32_t idx)
593802Salexander.borisov@nginx.com {
594802Salexander.borisov@nginx.com     const char   *name, *value;
595802Salexander.borisov@nginx.com     napi_value   str, vstr;
596802Salexander.borisov@nginx.com     napi_status  status;
597802Salexander.borisov@nginx.com 
598802Salexander.borisov@nginx.com     value = (const char *) nxt_unit_sptr_get(&f->value);
599802Salexander.borisov@nginx.com 
600802Salexander.borisov@nginx.com     status = napi_create_string_latin1(env_, value, f->value_length, &vstr);
601802Salexander.borisov@nginx.com     if (status != napi_ok) {
602802Salexander.borisov@nginx.com         return status;
603802Salexander.borisov@nginx.com     }
604802Salexander.borisov@nginx.com 
605802Salexander.borisov@nginx.com     name = (const char *) nxt_unit_sptr_get(&f->name);
606802Salexander.borisov@nginx.com 
607802Salexander.borisov@nginx.com     status = napi_set_named_property(env_, headers, name, vstr);
608802Salexander.borisov@nginx.com     if (status != napi_ok) {
609802Salexander.borisov@nginx.com         return status;
610802Salexander.borisov@nginx.com     }
611802Salexander.borisov@nginx.com 
612802Salexander.borisov@nginx.com     status = napi_create_string_latin1(env_, name, f->name_length, &str);
613802Salexander.borisov@nginx.com     if (status != napi_ok) {
614802Salexander.borisov@nginx.com         return status;
615802Salexander.borisov@nginx.com     }
616802Salexander.borisov@nginx.com 
617802Salexander.borisov@nginx.com     status = napi_set_element(env_, raw_headers, idx * 2, str);
618802Salexander.borisov@nginx.com     if (status != napi_ok) {
619802Salexander.borisov@nginx.com         return status;
620802Salexander.borisov@nginx.com     }
621802Salexander.borisov@nginx.com 
622802Salexander.borisov@nginx.com     status = napi_set_element(env_, raw_headers, idx * 2 + 1, vstr);
623802Salexander.borisov@nginx.com     if (status != napi_ok) {
624802Salexander.borisov@nginx.com         return status;
625802Salexander.borisov@nginx.com     }
626802Salexander.borisov@nginx.com 
627802Salexander.borisov@nginx.com     return napi_ok;
628802Salexander.borisov@nginx.com }
629802Salexander.borisov@nginx.com 
630802Salexander.borisov@nginx.com 
631802Salexander.borisov@nginx.com napi_value
632802Salexander.borisov@nginx.com Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req)
633802Salexander.borisov@nginx.com {
634828Salexander.borisov@nginx.com     napi_value   constructor, return_val, req_pointer;
635802Salexander.borisov@nginx.com     napi_status  status;
636802Salexander.borisov@nginx.com 
637802Salexander.borisov@nginx.com     status = napi_get_named_property(env_, server_obj, "socket",
638802Salexander.borisov@nginx.com                                      &constructor);
639802Salexander.borisov@nginx.com     if (status != napi_ok) {
640802Salexander.borisov@nginx.com         return nullptr;
641802Salexander.borisov@nginx.com     }
642802Salexander.borisov@nginx.com 
643802Salexander.borisov@nginx.com     status = napi_new_instance(env_, constructor, 0, NULL, &return_val);
644802Salexander.borisov@nginx.com     if (status != napi_ok) {
645802Salexander.borisov@nginx.com         return nullptr;
646802Salexander.borisov@nginx.com     }
647802Salexander.borisov@nginx.com 
648828Salexander.borisov@nginx.com     status = napi_create_int64(env_, (uintptr_t) req, &req_pointer);
649828Salexander.borisov@nginx.com     if (status != napi_ok) {
650828Salexander.borisov@nginx.com         return nullptr;
651828Salexander.borisov@nginx.com     }
652828Salexander.borisov@nginx.com 
653828Salexander.borisov@nginx.com     status = napi_set_named_property(env_, return_val, "req_pointer",
654828Salexander.borisov@nginx.com                                      req_pointer);
655828Salexander.borisov@nginx.com     if (status != napi_ok) {
656828Salexander.borisov@nginx.com         return nullptr;
657828Salexander.borisov@nginx.com     }
658828Salexander.borisov@nginx.com 
659802Salexander.borisov@nginx.com     return return_val;
660802Salexander.borisov@nginx.com }
661802Salexander.borisov@nginx.com 
662802Salexander.borisov@nginx.com 
663802Salexander.borisov@nginx.com napi_value
664802Salexander.borisov@nginx.com Unit::create_request(napi_value server_obj, napi_value socket)
665802Salexander.borisov@nginx.com {
666802Salexander.borisov@nginx.com     napi_value   constructor, return_val;
667802Salexander.borisov@nginx.com     napi_status  status;
668802Salexander.borisov@nginx.com 
669802Salexander.borisov@nginx.com     status = napi_get_named_property(env_, server_obj, "request",
670802Salexander.borisov@nginx.com                                      &constructor);
671802Salexander.borisov@nginx.com     if (status != napi_ok) {
672802Salexander.borisov@nginx.com         return nullptr;
673802Salexander.borisov@nginx.com     }
674802Salexander.borisov@nginx.com 
675802Salexander.borisov@nginx.com     status = napi_new_instance(env_, constructor, 1, &server_obj,
676802Salexander.borisov@nginx.com                                &return_val);
677802Salexander.borisov@nginx.com     if (status != napi_ok) {
678802Salexander.borisov@nginx.com         return nullptr;
679802Salexander.borisov@nginx.com     }
680802Salexander.borisov@nginx.com 
681802Salexander.borisov@nginx.com     status = napi_set_named_property(env_, return_val, "socket", socket);
682802Salexander.borisov@nginx.com     if (status != napi_ok) {
683802Salexander.borisov@nginx.com         return nullptr;
684802Salexander.borisov@nginx.com     }
685802Salexander.borisov@nginx.com 
686802Salexander.borisov@nginx.com     return return_val;
687802Salexander.borisov@nginx.com }
688802Salexander.borisov@nginx.com 
689802Salexander.borisov@nginx.com 
690802Salexander.borisov@nginx.com napi_value
691802Salexander.borisov@nginx.com Unit::create_response(napi_value server_obj, napi_value socket,
692802Salexander.borisov@nginx.com                       napi_value request, nxt_unit_request_info_t *req,
693802Salexander.borisov@nginx.com                       Unit *obj)
694802Salexander.borisov@nginx.com {
695802Salexander.borisov@nginx.com     napi_value   constructor, return_val, req_num;
696802Salexander.borisov@nginx.com     napi_status  status;
697802Salexander.borisov@nginx.com 
698802Salexander.borisov@nginx.com     status = napi_get_named_property(env_, server_obj, "response",
699802Salexander.borisov@nginx.com                                      &constructor);
700802Salexander.borisov@nginx.com     if (status != napi_ok) {
701802Salexander.borisov@nginx.com         return nullptr;
702802Salexander.borisov@nginx.com     }
703802Salexander.borisov@nginx.com 
704802Salexander.borisov@nginx.com     status = napi_new_instance(env_, constructor, 1, &request, &return_val);
705802Salexander.borisov@nginx.com     if (status != napi_ok) {
706802Salexander.borisov@nginx.com         return nullptr;
707802Salexander.borisov@nginx.com     }
708802Salexander.borisov@nginx.com 
709802Salexander.borisov@nginx.com     status = napi_set_named_property(env_, return_val, "socket", socket);
710802Salexander.borisov@nginx.com     if (status != napi_ok) {
711802Salexander.borisov@nginx.com         return nullptr;
712802Salexander.borisov@nginx.com     }
713802Salexander.borisov@nginx.com 
714802Salexander.borisov@nginx.com     status = napi_create_int64(env_, (int64_t) (uintptr_t) req, &req_num);
715802Salexander.borisov@nginx.com     if (status != napi_ok) {
716802Salexander.borisov@nginx.com         return nullptr;
717802Salexander.borisov@nginx.com     }
718802Salexander.borisov@nginx.com 
719802Salexander.borisov@nginx.com     status = napi_set_named_property(env_, return_val, "_req_point", req_num);
720802Salexander.borisov@nginx.com     if (status != napi_ok) {
721802Salexander.borisov@nginx.com         return nullptr;
722802Salexander.borisov@nginx.com     }
723802Salexander.borisov@nginx.com 
724802Salexander.borisov@nginx.com     return return_val;
725802Salexander.borisov@nginx.com }
726802Salexander.borisov@nginx.com 
727802Salexander.borisov@nginx.com 
728802Salexander.borisov@nginx.com napi_value
729802Salexander.borisov@nginx.com Unit::response_send_headers(napi_env env, napi_callback_info info)
730802Salexander.borisov@nginx.com {
731802Salexander.borisov@nginx.com     int                      ret;
732802Salexander.borisov@nginx.com     char                     *ptr, *name_ptr;
733802Salexander.borisov@nginx.com     bool                     is_array;
734802Salexander.borisov@nginx.com     size_t                   argc, name_len, value_len;
735802Salexander.borisov@nginx.com     int64_t                  req_p;
736802Salexander.borisov@nginx.com     uint32_t                 status_code, header_len, keys_len, array_len;
737802Salexander.borisov@nginx.com     uint32_t                 keys_count, i, j;
738802Salexander.borisov@nginx.com     uint16_t                 hash;
739802Salexander.borisov@nginx.com     napi_value               this_arg, headers, keys, name, value, array_val;
740802Salexander.borisov@nginx.com     napi_value               req_num;
741802Salexander.borisov@nginx.com     napi_status              status;
742843Salexander.borisov@nginx.com     napi_valuetype           val_type;
743802Salexander.borisov@nginx.com     nxt_unit_field_t         *f;
744802Salexander.borisov@nginx.com     nxt_unit_request_info_t  *req;
745802Salexander.borisov@nginx.com     napi_value               argv[5];
746802Salexander.borisov@nginx.com 
747802Salexander.borisov@nginx.com     argc = 5;
748802Salexander.borisov@nginx.com 
749802Salexander.borisov@nginx.com     status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL);
750802Salexander.borisov@nginx.com     if (status != napi_ok) {
751802Salexander.borisov@nginx.com         return nullptr;
752802Salexander.borisov@nginx.com     }
753802Salexander.borisov@nginx.com 
754802Salexander.borisov@nginx.com     if (argc != 5) {
755802Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Wrong args count. Need three: "
756802Salexander.borisov@nginx.com                          "statusCode, headers, headers count, headers length");
757802Salexander.borisov@nginx.com         return nullptr;
758802Salexander.borisov@nginx.com     }
759802Salexander.borisov@nginx.com 
760802Salexander.borisov@nginx.com     status = napi_get_named_property(env, argv[0], "_req_point", &req_num);
761802Salexander.borisov@nginx.com     if (status != napi_ok) {
762802Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Failed to get request pointer");
763802Salexander.borisov@nginx.com         return nullptr;
764802Salexander.borisov@nginx.com     }
765802Salexander.borisov@nginx.com 
766802Salexander.borisov@nginx.com     status = napi_get_value_int64(env, req_num, &req_p);
767802Salexander.borisov@nginx.com     if (status != napi_ok) {
768802Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Failed to get request pointer");
769802Salexander.borisov@nginx.com         return nullptr;
770802Salexander.borisov@nginx.com     }
771802Salexander.borisov@nginx.com 
772802Salexander.borisov@nginx.com     req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
773802Salexander.borisov@nginx.com 
774802Salexander.borisov@nginx.com     status = napi_get_value_uint32(env, argv[1], &status_code);
775802Salexander.borisov@nginx.com     if (status != napi_ok) {
776802Salexander.borisov@nginx.com         goto failed;
777802Salexander.borisov@nginx.com     }
778802Salexander.borisov@nginx.com 
779802Salexander.borisov@nginx.com     status = napi_get_value_uint32(env, argv[3], &keys_count);
780802Salexander.borisov@nginx.com     if (status != napi_ok) {
781802Salexander.borisov@nginx.com         goto failed;
782802Salexander.borisov@nginx.com     }
783802Salexander.borisov@nginx.com 
784802Salexander.borisov@nginx.com     status = napi_get_value_uint32(env, argv[4], &header_len);
785802Salexander.borisov@nginx.com     if (status != napi_ok) {
786802Salexander.borisov@nginx.com         goto failed;
787802Salexander.borisov@nginx.com     }
788802Salexander.borisov@nginx.com 
789802Salexander.borisov@nginx.com     /* Need to reserve extra byte for C-string 0-termination. */
790802Salexander.borisov@nginx.com     header_len++;
791802Salexander.borisov@nginx.com 
792802Salexander.borisov@nginx.com     headers = argv[2];
793802Salexander.borisov@nginx.com 
794802Salexander.borisov@nginx.com     ret = nxt_unit_response_init(req, status_code, keys_count, header_len);
795802Salexander.borisov@nginx.com     if (ret != NXT_UNIT_OK) {
796802Salexander.borisov@nginx.com         goto failed;
797802Salexander.borisov@nginx.com     }
798802Salexander.borisov@nginx.com 
799802Salexander.borisov@nginx.com     status = napi_get_property_names(env, headers, &keys);
800802Salexander.borisov@nginx.com     if (status != napi_ok) {
801802Salexander.borisov@nginx.com         goto failed;
802802Salexander.borisov@nginx.com     }
803802Salexander.borisov@nginx.com 
804802Salexander.borisov@nginx.com     status = napi_get_array_length(env, keys, &keys_len);
805802Salexander.borisov@nginx.com     if (status != napi_ok) {
806802Salexander.borisov@nginx.com         goto failed;
807802Salexander.borisov@nginx.com     }
808802Salexander.borisov@nginx.com 
809802Salexander.borisov@nginx.com     ptr = req->response_buf->free;
810802Salexander.borisov@nginx.com 
811802Salexander.borisov@nginx.com     for (i = 0; i < keys_len; i++) {
812802Salexander.borisov@nginx.com         status = napi_get_element(env, keys, i, &name);
813802Salexander.borisov@nginx.com         if (status != napi_ok) {
814802Salexander.borisov@nginx.com             goto failed;
815802Salexander.borisov@nginx.com         }
816802Salexander.borisov@nginx.com 
817802Salexander.borisov@nginx.com         status = napi_get_property(env, headers, name, &value);
818802Salexander.borisov@nginx.com         if (status != napi_ok) {
819802Salexander.borisov@nginx.com             goto failed;
820802Salexander.borisov@nginx.com         }
821802Salexander.borisov@nginx.com 
822802Salexander.borisov@nginx.com         status = napi_get_value_string_latin1(env, name, ptr, header_len,
823802Salexander.borisov@nginx.com                                               &name_len);
824802Salexander.borisov@nginx.com         if (status != napi_ok) {
825802Salexander.borisov@nginx.com             goto failed;
826802Salexander.borisov@nginx.com         }
827802Salexander.borisov@nginx.com 
828802Salexander.borisov@nginx.com         name_ptr = ptr;
829802Salexander.borisov@nginx.com 
830802Salexander.borisov@nginx.com         ptr += name_len;
831802Salexander.borisov@nginx.com         header_len -= name_len;
832802Salexander.borisov@nginx.com 
833802Salexander.borisov@nginx.com         hash = nxt_unit_field_hash(name_ptr, name_len);
834802Salexander.borisov@nginx.com 
835802Salexander.borisov@nginx.com         status = napi_is_array(env, value, &is_array);
836802Salexander.borisov@nginx.com         if (status != napi_ok) {
837802Salexander.borisov@nginx.com             goto failed;
838802Salexander.borisov@nginx.com         }
839802Salexander.borisov@nginx.com 
840802Salexander.borisov@nginx.com         if (is_array) {
841802Salexander.borisov@nginx.com             status = napi_get_array_length(env, value, &array_len);
842802Salexander.borisov@nginx.com             if (status != napi_ok) {
843802Salexander.borisov@nginx.com                 goto failed;
844802Salexander.borisov@nginx.com             }
845802Salexander.borisov@nginx.com 
846802Salexander.borisov@nginx.com             for (j = 0; j < array_len; j++) {
847802Salexander.borisov@nginx.com                 status = napi_get_element(env, value, j, &array_val);
848802Salexander.borisov@nginx.com                 if (status != napi_ok) {
849802Salexander.borisov@nginx.com                     goto failed;
850802Salexander.borisov@nginx.com                 }
851802Salexander.borisov@nginx.com 
852843Salexander.borisov@nginx.com                 napi_typeof(env, array_val, &val_type);
853843Salexander.borisov@nginx.com                 if (status != napi_ok) {
854843Salexander.borisov@nginx.com                     goto failed;
855843Salexander.borisov@nginx.com                 }
856843Salexander.borisov@nginx.com 
857843Salexander.borisov@nginx.com                 if (val_type != napi_string) {
858843Salexander.borisov@nginx.com                     status = napi_coerce_to_string(env, array_val, &array_val);
859843Salexander.borisov@nginx.com                     if (status != napi_ok) {
860843Salexander.borisov@nginx.com                         goto failed;
861843Salexander.borisov@nginx.com                     }
862843Salexander.borisov@nginx.com                 }
863843Salexander.borisov@nginx.com 
864802Salexander.borisov@nginx.com                 status = napi_get_value_string_latin1(env, array_val, ptr,
865802Salexander.borisov@nginx.com                                                       header_len,
866802Salexander.borisov@nginx.com                                                       &value_len);
867802Salexander.borisov@nginx.com                 if (status != napi_ok) {
868802Salexander.borisov@nginx.com                     goto failed;
869802Salexander.borisov@nginx.com                 }
870802Salexander.borisov@nginx.com 
871802Salexander.borisov@nginx.com                 f = req->response->fields + req->response->fields_count;
872802Salexander.borisov@nginx.com                 f->skip = 0;
873802Salexander.borisov@nginx.com 
874802Salexander.borisov@nginx.com                 nxt_unit_sptr_set(&f->name, name_ptr);
875802Salexander.borisov@nginx.com 
876802Salexander.borisov@nginx.com                 f->name_length = name_len;
877802Salexander.borisov@nginx.com                 f->hash = hash;
878802Salexander.borisov@nginx.com 
879802Salexander.borisov@nginx.com                 nxt_unit_sptr_set(&f->value, ptr);
880802Salexander.borisov@nginx.com                 f->value_length = (uint32_t) value_len;
881802Salexander.borisov@nginx.com 
882802Salexander.borisov@nginx.com                 ptr += value_len;
883802Salexander.borisov@nginx.com                 header_len -= value_len;
884802Salexander.borisov@nginx.com 
885802Salexander.borisov@nginx.com                 req->response->fields_count++;
886802Salexander.borisov@nginx.com             }
887802Salexander.borisov@nginx.com 
888802Salexander.borisov@nginx.com         } else {
889843Salexander.borisov@nginx.com             napi_typeof(env, value, &val_type);
890843Salexander.borisov@nginx.com             if (status != napi_ok) {
891843Salexander.borisov@nginx.com                 goto failed;
892843Salexander.borisov@nginx.com             }
893843Salexander.borisov@nginx.com 
894843Salexander.borisov@nginx.com             if (val_type != napi_string) {
895843Salexander.borisov@nginx.com                 status = napi_coerce_to_string(env, value, &value);
896843Salexander.borisov@nginx.com                 if (status != napi_ok) {
897843Salexander.borisov@nginx.com                     goto failed;
898843Salexander.borisov@nginx.com                 }
899843Salexander.borisov@nginx.com             }
900843Salexander.borisov@nginx.com 
901802Salexander.borisov@nginx.com             status = napi_get_value_string_latin1(env, value, ptr, header_len,
902802Salexander.borisov@nginx.com                                                   &value_len);
903802Salexander.borisov@nginx.com             if (status != napi_ok) {
904802Salexander.borisov@nginx.com                 goto failed;
905802Salexander.borisov@nginx.com             }
906802Salexander.borisov@nginx.com 
907802Salexander.borisov@nginx.com             f = req->response->fields + req->response->fields_count;
908802Salexander.borisov@nginx.com             f->skip = 0;
909802Salexander.borisov@nginx.com 
910802Salexander.borisov@nginx.com             nxt_unit_sptr_set(&f->name, name_ptr);
911802Salexander.borisov@nginx.com 
912802Salexander.borisov@nginx.com             f->name_length = name_len;
913802Salexander.borisov@nginx.com             f->hash = hash;
914802Salexander.borisov@nginx.com 
915802Salexander.borisov@nginx.com             nxt_unit_sptr_set(&f->value, ptr);
916802Salexander.borisov@nginx.com             f->value_length = (uint32_t) value_len;
917802Salexander.borisov@nginx.com 
918802Salexander.borisov@nginx.com             ptr += value_len;
919802Salexander.borisov@nginx.com             header_len -= value_len;
920802Salexander.borisov@nginx.com 
921802Salexander.borisov@nginx.com             req->response->fields_count++;
922802Salexander.borisov@nginx.com         }
923802Salexander.borisov@nginx.com     }
924802Salexander.borisov@nginx.com 
925802Salexander.borisov@nginx.com     req->response_buf->free = ptr;
926802Salexander.borisov@nginx.com 
927802Salexander.borisov@nginx.com     ret = nxt_unit_response_send(req);
928802Salexander.borisov@nginx.com     if (ret != NXT_UNIT_OK) {
929802Salexander.borisov@nginx.com         goto failed;
930802Salexander.borisov@nginx.com     }
931802Salexander.borisov@nginx.com 
932802Salexander.borisov@nginx.com     return this_arg;
933802Salexander.borisov@nginx.com 
934802Salexander.borisov@nginx.com failed:
935802Salexander.borisov@nginx.com 
936802Salexander.borisov@nginx.com     req->response->fields_count = 0;
937802Salexander.borisov@nginx.com 
938802Salexander.borisov@nginx.com     napi_throw_error(env, NULL, "Failed to write headers");
939802Salexander.borisov@nginx.com 
940802Salexander.borisov@nginx.com     return nullptr;
941802Salexander.borisov@nginx.com }
942802Salexander.borisov@nginx.com 
943802Salexander.borisov@nginx.com 
944802Salexander.borisov@nginx.com napi_value
945802Salexander.borisov@nginx.com Unit::response_write(napi_env env, napi_callback_info info)
946802Salexander.borisov@nginx.com {
947802Salexander.borisov@nginx.com     int                      ret;
948802Salexander.borisov@nginx.com     char                     *ptr;
949802Salexander.borisov@nginx.com     size_t                   argc, have_buf_len;
950802Salexander.borisov@nginx.com     int64_t                  req_p;
951802Salexander.borisov@nginx.com     uint32_t                 buf_len;
952802Salexander.borisov@nginx.com     napi_value               this_arg, req_num;
953802Salexander.borisov@nginx.com     napi_status              status;
954802Salexander.borisov@nginx.com     nxt_unit_buf_t           *buf;
955802Salexander.borisov@nginx.com     napi_valuetype           buf_type;
956802Salexander.borisov@nginx.com     nxt_unit_request_info_t  *req;
957802Salexander.borisov@nginx.com     napi_value               argv[3];
958802Salexander.borisov@nginx.com 
959802Salexander.borisov@nginx.com     argc = 3;
960802Salexander.borisov@nginx.com 
961802Salexander.borisov@nginx.com     status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL);
962802Salexander.borisov@nginx.com     if (status != napi_ok) {
963802Salexander.borisov@nginx.com         goto failed;
964802Salexander.borisov@nginx.com     }
965802Salexander.borisov@nginx.com 
966802Salexander.borisov@nginx.com     if (argc != 3) {
967802Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Wrong args count. Need two: "
968802Salexander.borisov@nginx.com                          "chunk, chunk length");
969802Salexander.borisov@nginx.com         return nullptr;
970802Salexander.borisov@nginx.com     }
971802Salexander.borisov@nginx.com 
972802Salexander.borisov@nginx.com     status = napi_get_named_property(env, argv[0], "_req_point", &req_num);
973802Salexander.borisov@nginx.com     if (status != napi_ok) {
974802Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Failed to get request pointer");
975802Salexander.borisov@nginx.com         return nullptr;
976802Salexander.borisov@nginx.com     }
977802Salexander.borisov@nginx.com 
978802Salexander.borisov@nginx.com     status = napi_get_value_int64(env, req_num, &req_p);
979802Salexander.borisov@nginx.com     if (status != napi_ok) {
980802Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Failed to get request pointer");
981802Salexander.borisov@nginx.com         return nullptr;
982802Salexander.borisov@nginx.com     }
983802Salexander.borisov@nginx.com 
984802Salexander.borisov@nginx.com     req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
985802Salexander.borisov@nginx.com 
986802Salexander.borisov@nginx.com     status = napi_get_value_uint32(env, argv[2], &buf_len);
987802Salexander.borisov@nginx.com     if (status != napi_ok) {
988802Salexander.borisov@nginx.com         goto failed;
989802Salexander.borisov@nginx.com     }
990802Salexander.borisov@nginx.com 
991802Salexander.borisov@nginx.com     status = napi_typeof(env, argv[1], &buf_type);
992802Salexander.borisov@nginx.com     if (status != napi_ok) {
993802Salexander.borisov@nginx.com         goto failed;
994802Salexander.borisov@nginx.com     }
995802Salexander.borisov@nginx.com 
996802Salexander.borisov@nginx.com     buf_len++;
997802Salexander.borisov@nginx.com 
998802Salexander.borisov@nginx.com     buf = nxt_unit_response_buf_alloc(req, buf_len);
999802Salexander.borisov@nginx.com     if (buf == NULL) {
1000802Salexander.borisov@nginx.com         goto failed;
1001802Salexander.borisov@nginx.com     }
1002802Salexander.borisov@nginx.com 
1003802Salexander.borisov@nginx.com     if (buf_type == napi_string) {
1004802Salexander.borisov@nginx.com         /* TODO: will work only for utf8 content-type */
1005802Salexander.borisov@nginx.com 
1006802Salexander.borisov@nginx.com         status = napi_get_value_string_utf8(env, argv[1], buf->free,
1007802Salexander.borisov@nginx.com                                             buf_len, &have_buf_len);
1008802Salexander.borisov@nginx.com 
1009802Salexander.borisov@nginx.com     } else {
1010802Salexander.borisov@nginx.com         status = napi_get_buffer_info(env, argv[1], (void **) &ptr,
1011802Salexander.borisov@nginx.com                                       &have_buf_len);
1012802Salexander.borisov@nginx.com 
1013802Salexander.borisov@nginx.com         memcpy(buf->free, ptr, have_buf_len);
1014802Salexander.borisov@nginx.com     }
1015802Salexander.borisov@nginx.com 
1016802Salexander.borisov@nginx.com     if (status != napi_ok) {
1017802Salexander.borisov@nginx.com         goto failed;
1018802Salexander.borisov@nginx.com     }
1019802Salexander.borisov@nginx.com 
1020802Salexander.borisov@nginx.com     buf->free += have_buf_len;
1021802Salexander.borisov@nginx.com 
1022802Salexander.borisov@nginx.com     ret = nxt_unit_buf_send(buf);
1023802Salexander.borisov@nginx.com     if (ret != NXT_UNIT_OK) {
1024802Salexander.borisov@nginx.com         goto failed;
1025802Salexander.borisov@nginx.com     }
1026802Salexander.borisov@nginx.com 
1027802Salexander.borisov@nginx.com     return this_arg;
1028802Salexander.borisov@nginx.com 
1029802Salexander.borisov@nginx.com failed:
1030802Salexander.borisov@nginx.com 
1031802Salexander.borisov@nginx.com     napi_throw_error(env, NULL, "Failed to write body");
1032802Salexander.borisov@nginx.com 
1033802Salexander.borisov@nginx.com     return nullptr;
1034802Salexander.borisov@nginx.com }
1035802Salexander.borisov@nginx.com 
1036802Salexander.borisov@nginx.com 
1037802Salexander.borisov@nginx.com napi_value
1038802Salexander.borisov@nginx.com Unit::response_end(napi_env env, napi_callback_info info)
1039802Salexander.borisov@nginx.com {
1040802Salexander.borisov@nginx.com     size_t                   argc;
1041802Salexander.borisov@nginx.com     int64_t                  req_p;
1042802Salexander.borisov@nginx.com     napi_value               resp, this_arg, req_num;
1043802Salexander.borisov@nginx.com     napi_status              status;
1044802Salexander.borisov@nginx.com     nxt_unit_request_info_t  *req;
1045802Salexander.borisov@nginx.com 
1046802Salexander.borisov@nginx.com     argc = 1;
1047802Salexander.borisov@nginx.com 
1048802Salexander.borisov@nginx.com     status = napi_get_cb_info(env, info, &argc, &resp, &this_arg, NULL);
1049802Salexander.borisov@nginx.com     if (status != napi_ok) {
1050802Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Failed to finalize sending body");
1051802Salexander.borisov@nginx.com         return nullptr;
1052802Salexander.borisov@nginx.com     }
1053802Salexander.borisov@nginx.com 
1054802Salexander.borisov@nginx.com     status = napi_get_named_property(env, resp, "_req_point", &req_num);
1055802Salexander.borisov@nginx.com     if (status != napi_ok) {
1056802Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Failed to get request pointer");
1057802Salexander.borisov@nginx.com         return nullptr;
1058802Salexander.borisov@nginx.com     }
1059802Salexander.borisov@nginx.com 
1060802Salexander.borisov@nginx.com     status = napi_get_value_int64(env, req_num, &req_p);
1061802Salexander.borisov@nginx.com     if (status != napi_ok) {
1062802Salexander.borisov@nginx.com         napi_throw_error(env, NULL, "Failed to get request pointer");
1063802Salexander.borisov@nginx.com         return nullptr;
1064802Salexander.borisov@nginx.com     }
1065802Salexander.borisov@nginx.com 
1066802Salexander.borisov@nginx.com     req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
1067802Salexander.borisov@nginx.com 
1068802Salexander.borisov@nginx.com     nxt_unit_request_done(req, NXT_UNIT_OK);
1069802Salexander.borisov@nginx.com 
1070802Salexander.borisov@nginx.com     return this_arg;
1071802Salexander.borisov@nginx.com }
1072