xref: /unit/src/nodejs/unit-http/unit.cpp (revision 1038)
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 
231020Smax.romanov@nginx.com Unit::Unit(napi_env env, napi_value jsthis):
241020Smax.romanov@nginx.com     nxt_napi(env),
251020Smax.romanov@nginx.com     wrapper_(wrap(jsthis, this, destroy)),
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 {
331020Smax.romanov@nginx.com     delete_reference(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 {
401020Smax.romanov@nginx.com     nxt_napi    napi(env);
411020Smax.romanov@nginx.com     napi_value  cons;
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 
491020Smax.romanov@nginx.com     try {
501020Smax.romanov@nginx.com         cons = napi.define_class("Unit", create, 3, properties);
511020Smax.romanov@nginx.com         constructor_ = napi.create_reference(cons);
52802Salexander.borisov@nginx.com 
531020Smax.romanov@nginx.com         napi.set_named_property(exports, "Unit", cons);
541020Smax.romanov@nginx.com         napi.set_named_property(exports, "unit_response_headers",
551020Smax.romanov@nginx.com                                 response_send_headers);
561020Smax.romanov@nginx.com         napi.set_named_property(exports, "unit_response_write", response_write);
571020Smax.romanov@nginx.com         napi.set_named_property(exports, "unit_response_end", response_end);
58802Salexander.borisov@nginx.com 
591020Smax.romanov@nginx.com     } catch (exception &e) {
601020Smax.romanov@nginx.com         napi.throw_error(e);
611020Smax.romanov@nginx.com         return nullptr;
62802Salexander.borisov@nginx.com     }
63802Salexander.borisov@nginx.com 
64802Salexander.borisov@nginx.com     return exports;
65802Salexander.borisov@nginx.com }
66802Salexander.borisov@nginx.com 
67802Salexander.borisov@nginx.com 
68802Salexander.borisov@nginx.com void
69802Salexander.borisov@nginx.com Unit::destroy(napi_env env, void *nativeObject, void *finalize_hint)
70802Salexander.borisov@nginx.com {
71802Salexander.borisov@nginx.com     Unit  *obj = reinterpret_cast<Unit *>(nativeObject);
72802Salexander.borisov@nginx.com 
73802Salexander.borisov@nginx.com     delete obj;
74802Salexander.borisov@nginx.com }
75802Salexander.borisov@nginx.com 
76802Salexander.borisov@nginx.com 
77802Salexander.borisov@nginx.com napi_value
78802Salexander.borisov@nginx.com Unit::create(napi_env env, napi_callback_info info)
79802Salexander.borisov@nginx.com {
801020Smax.romanov@nginx.com     nxt_napi    napi(env);
811020Smax.romanov@nginx.com     napi_value  target, cons, instance, jsthis;
821020Smax.romanov@nginx.com 
831020Smax.romanov@nginx.com     try {
841020Smax.romanov@nginx.com         target = napi.get_new_target(info);
85802Salexander.borisov@nginx.com 
861020Smax.romanov@nginx.com         if (target != nullptr) {
871020Smax.romanov@nginx.com             /* Invoked as constructor: `new Unit(...)`. */
881020Smax.romanov@nginx.com             jsthis = napi.get_cb_info(info);
89802Salexander.borisov@nginx.com 
901023Smax.romanov@nginx.com             new Unit(env, jsthis);
911023Smax.romanov@nginx.com             napi.create_reference(jsthis);
921020Smax.romanov@nginx.com 
931020Smax.romanov@nginx.com             return jsthis;
94802Salexander.borisov@nginx.com         }
95802Salexander.borisov@nginx.com 
961020Smax.romanov@nginx.com         /* Invoked as plain function `Unit(...)`, turn into construct call. */
971020Smax.romanov@nginx.com         cons = napi.get_reference_value(constructor_);
981020Smax.romanov@nginx.com         instance = napi.new_instance(cons);
991023Smax.romanov@nginx.com         napi.create_reference(instance);
100841Salexander.borisov@nginx.com 
1011020Smax.romanov@nginx.com     } catch (exception &e) {
1021020Smax.romanov@nginx.com         napi.throw_error(e);
1031020Smax.romanov@nginx.com         return nullptr;
104841Salexander.borisov@nginx.com     }
105841Salexander.borisov@nginx.com 
106802Salexander.borisov@nginx.com     return instance;
107802Salexander.borisov@nginx.com }
108802Salexander.borisov@nginx.com 
109802Salexander.borisov@nginx.com 
110802Salexander.borisov@nginx.com napi_value
111802Salexander.borisov@nginx.com Unit::create_server(napi_env env, napi_callback_info info)
112802Salexander.borisov@nginx.com {
113802Salexander.borisov@nginx.com     Unit             *obj;
114802Salexander.borisov@nginx.com     size_t           argc;
1151020Smax.romanov@nginx.com     nxt_napi         napi(env);
116828Salexander.borisov@nginx.com     napi_value       jsthis, argv;
117802Salexander.borisov@nginx.com     nxt_unit_init_t  unit_init;
118802Salexander.borisov@nginx.com 
119802Salexander.borisov@nginx.com     argc = 1;
120802Salexander.borisov@nginx.com 
1211020Smax.romanov@nginx.com     try {
1221020Smax.romanov@nginx.com         jsthis = napi.get_cb_info(info, argc, &argv);
1231020Smax.romanov@nginx.com         obj = (Unit *) napi.unwrap(jsthis);
124802Salexander.borisov@nginx.com 
1251020Smax.romanov@nginx.com     } catch (exception &e) {
1261020Smax.romanov@nginx.com         napi.throw_error(e);
1271020Smax.romanov@nginx.com         return nullptr;
128802Salexander.borisov@nginx.com     }
129802Salexander.borisov@nginx.com 
130802Salexander.borisov@nginx.com     memset(&unit_init, 0, sizeof(nxt_unit_init_t));
131802Salexander.borisov@nginx.com 
132802Salexander.borisov@nginx.com     unit_init.data = obj;
133802Salexander.borisov@nginx.com     unit_init.callbacks.request_handler = request_handler;
134828Salexander.borisov@nginx.com     unit_init.callbacks.add_port        = add_port;
135828Salexander.borisov@nginx.com     unit_init.callbacks.remove_port     = remove_port;
136842Salexander.borisov@nginx.com     unit_init.callbacks.quit            = quit;
137802Salexander.borisov@nginx.com 
138802Salexander.borisov@nginx.com     obj->unit_ctx_ = nxt_unit_init(&unit_init);
139802Salexander.borisov@nginx.com     if (obj->unit_ctx_ == NULL) {
140802Salexander.borisov@nginx.com         goto failed;
141802Salexander.borisov@nginx.com     }
142802Salexander.borisov@nginx.com 
143802Salexander.borisov@nginx.com     return nullptr;
144802Salexander.borisov@nginx.com 
145802Salexander.borisov@nginx.com failed:
146802Salexander.borisov@nginx.com 
147802Salexander.borisov@nginx.com     napi_throw_error(env, NULL, "Failed to create Unit object");
148802Salexander.borisov@nginx.com 
149802Salexander.borisov@nginx.com     return nullptr;
150802Salexander.borisov@nginx.com }
151802Salexander.borisov@nginx.com 
152802Salexander.borisov@nginx.com 
153802Salexander.borisov@nginx.com napi_value
154802Salexander.borisov@nginx.com Unit::listen(napi_env env, napi_callback_info info)
155802Salexander.borisov@nginx.com {
156828Salexander.borisov@nginx.com     return nullptr;
157828Salexander.borisov@nginx.com }
158828Salexander.borisov@nginx.com 
159802Salexander.borisov@nginx.com 
160828Salexander.borisov@nginx.com napi_value
161828Salexander.borisov@nginx.com Unit::_read(napi_env env, napi_callback_info info)
162828Salexander.borisov@nginx.com {
163828Salexander.borisov@nginx.com     void                     *data;
164828Salexander.borisov@nginx.com     size_t                   argc;
1651020Smax.romanov@nginx.com     nxt_napi                 napi(env);
1661023Smax.romanov@nginx.com     napi_value               buffer, argv;
167828Salexander.borisov@nginx.com     nxt_unit_request_info_t  *req;
168828Salexander.borisov@nginx.com 
169828Salexander.borisov@nginx.com     argc = 1;
170828Salexander.borisov@nginx.com 
1711020Smax.romanov@nginx.com     try {
1721023Smax.romanov@nginx.com         napi.get_cb_info(info, argc, &argv);
173802Salexander.borisov@nginx.com 
1741020Smax.romanov@nginx.com         req = napi.get_request_info(argv);
1751020Smax.romanov@nginx.com         buffer = napi.create_buffer((size_t) req->content_length, &data);
176802Salexander.borisov@nginx.com 
1771020Smax.romanov@nginx.com     } catch (exception &e) {
1781020Smax.romanov@nginx.com         napi.throw_error(e);
179802Salexander.borisov@nginx.com         return nullptr;
180802Salexander.borisov@nginx.com     }
181802Salexander.borisov@nginx.com 
182828Salexander.borisov@nginx.com     nxt_unit_request_read(req, data, req->content_length);
183802Salexander.borisov@nginx.com 
184828Salexander.borisov@nginx.com     return buffer;
185802Salexander.borisov@nginx.com }
186802Salexander.borisov@nginx.com 
187802Salexander.borisov@nginx.com 
188802Salexander.borisov@nginx.com void
189802Salexander.borisov@nginx.com Unit::request_handler(nxt_unit_request_info_t *req)
190802Salexander.borisov@nginx.com {
1911020Smax.romanov@nginx.com     Unit         *obj;
1921020Smax.romanov@nginx.com     napi_value   socket, request, response, server_obj;
1931020Smax.romanov@nginx.com     napi_value   emit_events;
1941020Smax.romanov@nginx.com     napi_value   events_args[3];
195802Salexander.borisov@nginx.com 
196802Salexander.borisov@nginx.com     obj = reinterpret_cast<Unit *>(req->unit->data);
197802Salexander.borisov@nginx.com 
1981020Smax.romanov@nginx.com     try {
1991020Smax.romanov@nginx.com         nxt_handle_scope  scope(obj->env());
200802Salexander.borisov@nginx.com 
2011020Smax.romanov@nginx.com         server_obj = obj->get_server_object();
202802Salexander.borisov@nginx.com 
2031020Smax.romanov@nginx.com         socket = obj->create_socket(server_obj, req);
2041020Smax.romanov@nginx.com         request = obj->create_request(server_obj, socket);
2051020Smax.romanov@nginx.com         response = obj->create_response(server_obj, socket, request, req);
206802Salexander.borisov@nginx.com 
2071020Smax.romanov@nginx.com         obj->create_headers(req, request);
208802Salexander.borisov@nginx.com 
2091020Smax.romanov@nginx.com         emit_events = obj->get_named_property(server_obj, "emit_events");
210828Salexander.borisov@nginx.com 
2111020Smax.romanov@nginx.com         events_args[0] = server_obj;
2121020Smax.romanov@nginx.com         events_args[1] = request;
2131020Smax.romanov@nginx.com         events_args[2] = response;
214871Salexander.borisov@nginx.com 
2151020Smax.romanov@nginx.com         nxt_async_context   async_context(obj->env(), "unit_request_handler");
2161020Smax.romanov@nginx.com         nxt_callback_scope  async_scope(async_context);
217876Salexander.borisov@nginx.com 
2181020Smax.romanov@nginx.com         obj->make_callback(async_context, server_obj, emit_events,
2191020Smax.romanov@nginx.com                            3, events_args);
220871Salexander.borisov@nginx.com 
2211020Smax.romanov@nginx.com     } catch (exception &e) {
2221020Smax.romanov@nginx.com         obj->throw_error(e);
223871Salexander.borisov@nginx.com     }
224802Salexander.borisov@nginx.com }
225802Salexander.borisov@nginx.com 
226802Salexander.borisov@nginx.com 
227828Salexander.borisov@nginx.com void
228828Salexander.borisov@nginx.com nxt_uv_read_callback(uv_poll_t *handle, int status, int events)
229828Salexander.borisov@nginx.com {
230828Salexander.borisov@nginx.com     nxt_unit_run_once((nxt_unit_ctx_t *) handle->data);
231828Salexander.borisov@nginx.com }
232828Salexander.borisov@nginx.com 
233828Salexander.borisov@nginx.com 
234828Salexander.borisov@nginx.com int
235828Salexander.borisov@nginx.com Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
236828Salexander.borisov@nginx.com {
237842Salexander.borisov@nginx.com     int               err;
238842Salexander.borisov@nginx.com     Unit              *obj;
239842Salexander.borisov@nginx.com     uv_loop_t         *loop;
240842Salexander.borisov@nginx.com     napi_status       status;
241842Salexander.borisov@nginx.com     nxt_nodejs_ctx_t  *node_ctx;
242828Salexander.borisov@nginx.com 
243828Salexander.borisov@nginx.com     if (port->in_fd != -1) {
244828Salexander.borisov@nginx.com         obj = reinterpret_cast<Unit *>(ctx->unit->data);
245828Salexander.borisov@nginx.com 
246828Salexander.borisov@nginx.com         if (fcntl(port->in_fd, F_SETFL, O_NONBLOCK) == -1) {
2471020Smax.romanov@nginx.com             obj->throw_error("Failed to upgrade read"
248828Salexander.borisov@nginx.com                              " file descriptor to O_NONBLOCK");
249828Salexander.borisov@nginx.com             return -1;
250828Salexander.borisov@nginx.com         }
251828Salexander.borisov@nginx.com 
2521020Smax.romanov@nginx.com         status = napi_get_uv_event_loop(obj->env(), &loop);
253828Salexander.borisov@nginx.com         if (status != napi_ok) {
2541020Smax.romanov@nginx.com             obj->throw_error("Failed to get uv.loop");
255828Salexander.borisov@nginx.com             return NXT_UNIT_ERROR;
256828Salexander.borisov@nginx.com         }
257828Salexander.borisov@nginx.com 
258842Salexander.borisov@nginx.com         node_ctx = new nxt_nodejs_ctx_t;
259828Salexander.borisov@nginx.com 
260842Salexander.borisov@nginx.com         err = uv_poll_init(loop, &node_ctx->poll, port->in_fd);
261828Salexander.borisov@nginx.com         if (err < 0) {
2621020Smax.romanov@nginx.com             obj->throw_error("Failed to init uv.poll");
263828Salexander.borisov@nginx.com             return NXT_UNIT_ERROR;
264828Salexander.borisov@nginx.com         }
265828Salexander.borisov@nginx.com 
266842Salexander.borisov@nginx.com         err = uv_poll_start(&node_ctx->poll, UV_READABLE, nxt_uv_read_callback);
267828Salexander.borisov@nginx.com         if (err < 0) {
2681020Smax.romanov@nginx.com             obj->throw_error("Failed to start uv.poll");
269828Salexander.borisov@nginx.com             return NXT_UNIT_ERROR;
270828Salexander.borisov@nginx.com         }
271828Salexander.borisov@nginx.com 
272842Salexander.borisov@nginx.com         ctx->data = node_ctx;
273842Salexander.borisov@nginx.com 
274842Salexander.borisov@nginx.com         node_ctx->port_id = port->id;
275842Salexander.borisov@nginx.com         node_ctx->poll.data = ctx;
276828Salexander.borisov@nginx.com     }
277828Salexander.borisov@nginx.com 
278828Salexander.borisov@nginx.com     return nxt_unit_add_port(ctx, port);
279828Salexander.borisov@nginx.com }
280828Salexander.borisov@nginx.com 
281828Salexander.borisov@nginx.com 
2821008Szelenkov@nginx.com inline bool
283842Salexander.borisov@nginx.com operator == (const nxt_unit_port_id_t &p1, const nxt_unit_port_id_t &p2)
284842Salexander.borisov@nginx.com {
285842Salexander.borisov@nginx.com     return p1.pid == p2.pid && p1.id == p2.id;
286842Salexander.borisov@nginx.com }
287842Salexander.borisov@nginx.com 
288842Salexander.borisov@nginx.com 
289828Salexander.borisov@nginx.com void
290828Salexander.borisov@nginx.com Unit::remove_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id)
291828Salexander.borisov@nginx.com {
292842Salexander.borisov@nginx.com     nxt_nodejs_ctx_t  *node_ctx;
293842Salexander.borisov@nginx.com 
294842Salexander.borisov@nginx.com     if (ctx->data != NULL) {
295842Salexander.borisov@nginx.com         node_ctx = (nxt_nodejs_ctx_t *) ctx->data;
296828Salexander.borisov@nginx.com 
297842Salexander.borisov@nginx.com         if (node_ctx->port_id == *port_id) {
298842Salexander.borisov@nginx.com             uv_poll_stop(&node_ctx->poll);
299828Salexander.borisov@nginx.com 
300842Salexander.borisov@nginx.com             delete node_ctx;
301828Salexander.borisov@nginx.com 
302842Salexander.borisov@nginx.com             ctx->data = NULL;
303842Salexander.borisov@nginx.com         }
304828Salexander.borisov@nginx.com     }
305828Salexander.borisov@nginx.com 
306828Salexander.borisov@nginx.com     nxt_unit_remove_port(ctx, port_id);
307828Salexander.borisov@nginx.com }
308828Salexander.borisov@nginx.com 
309828Salexander.borisov@nginx.com 
310842Salexander.borisov@nginx.com void
311842Salexander.borisov@nginx.com Unit::quit(nxt_unit_ctx_t *ctx)
312842Salexander.borisov@nginx.com {
3131021Smax.romanov@nginx.com     Unit        *obj;
3141021Smax.romanov@nginx.com     napi_value  server_obj, emit_close;
3151021Smax.romanov@nginx.com 
3161021Smax.romanov@nginx.com     obj = reinterpret_cast<Unit *>(ctx->unit->data);
3171021Smax.romanov@nginx.com 
3181021Smax.romanov@nginx.com     try {
3191021Smax.romanov@nginx.com         nxt_handle_scope  scope(obj->env());
3201021Smax.romanov@nginx.com 
3211021Smax.romanov@nginx.com         server_obj = obj->get_server_object();
3221021Smax.romanov@nginx.com 
3231021Smax.romanov@nginx.com         emit_close = obj->get_named_property(server_obj, "emit_close");
3241021Smax.romanov@nginx.com 
3251021Smax.romanov@nginx.com         nxt_async_context   async_context(obj->env(), "unit_quit");
3261021Smax.romanov@nginx.com         nxt_callback_scope  async_scope(async_context);
3271021Smax.romanov@nginx.com 
3281021Smax.romanov@nginx.com         obj->make_callback(async_context, server_obj, emit_close, 0, NULL);
3291021Smax.romanov@nginx.com 
3301021Smax.romanov@nginx.com     } catch (exception &e) {
3311021Smax.romanov@nginx.com         obj->throw_error(e);
3321021Smax.romanov@nginx.com     }
3331021Smax.romanov@nginx.com 
334842Salexander.borisov@nginx.com     nxt_unit_done(ctx);
335842Salexander.borisov@nginx.com }
336842Salexander.borisov@nginx.com 
337842Salexander.borisov@nginx.com 
338802Salexander.borisov@nginx.com napi_value
339802Salexander.borisov@nginx.com Unit::get_server_object()
340802Salexander.borisov@nginx.com {
3411020Smax.romanov@nginx.com     napi_value  unit_obj;
342802Salexander.borisov@nginx.com 
3431020Smax.romanov@nginx.com     unit_obj = get_reference_value(wrapper_);
344802Salexander.borisov@nginx.com 
3451020Smax.romanov@nginx.com     return get_named_property(unit_obj, "server");
346802Salexander.borisov@nginx.com }
347802Salexander.borisov@nginx.com 
348802Salexander.borisov@nginx.com 
3491020Smax.romanov@nginx.com void
350802Salexander.borisov@nginx.com Unit::create_headers(nxt_unit_request_info_t *req, napi_value request)
351802Salexander.borisov@nginx.com {
352802Salexander.borisov@nginx.com     uint32_t            i;
3531020Smax.romanov@nginx.com     napi_value          headers, raw_headers;
354802Salexander.borisov@nginx.com     napi_status         status;
355802Salexander.borisov@nginx.com     nxt_unit_request_t  *r;
356802Salexander.borisov@nginx.com 
357802Salexander.borisov@nginx.com     r = req->request;
358802Salexander.borisov@nginx.com 
3591020Smax.romanov@nginx.com     headers = create_object();
360802Salexander.borisov@nginx.com 
3611020Smax.romanov@nginx.com     status = napi_create_array_with_length(env(), r->fields_count * 2,
362802Salexander.borisov@nginx.com                                            &raw_headers);
363802Salexander.borisov@nginx.com     if (status != napi_ok) {
3641020Smax.romanov@nginx.com         throw exception("Failed to create array");
365802Salexander.borisov@nginx.com     }
366802Salexander.borisov@nginx.com 
367802Salexander.borisov@nginx.com     for (i = 0; i < r->fields_count; i++) {
3681020Smax.romanov@nginx.com         append_header(r->fields + i, headers, raw_headers, i);
369802Salexander.borisov@nginx.com     }
370802Salexander.borisov@nginx.com 
3711020Smax.romanov@nginx.com     set_named_property(request, "headers", headers);
3721020Smax.romanov@nginx.com     set_named_property(request, "rawHeaders", raw_headers);
3731020Smax.romanov@nginx.com     set_named_property(request, "httpVersion", r->version, r->version_length);
3741020Smax.romanov@nginx.com     set_named_property(request, "method", r->method, r->method_length);
3751020Smax.romanov@nginx.com     set_named_property(request, "url", r->target, r->target_length);
376802Salexander.borisov@nginx.com }
377802Salexander.borisov@nginx.com 
378802Salexander.borisov@nginx.com 
379*1038Smax.romanov@nginx.com inline char
380*1038Smax.romanov@nginx.com lowcase(char c)
381*1038Smax.romanov@nginx.com {
382*1038Smax.romanov@nginx.com     return (c >= 'A' && c <= 'Z') ? (c | 0x20) : c;
383*1038Smax.romanov@nginx.com }
384*1038Smax.romanov@nginx.com 
385*1038Smax.romanov@nginx.com 
3861020Smax.romanov@nginx.com inline void
387802Salexander.borisov@nginx.com Unit::append_header(nxt_unit_field_t *f, napi_value headers,
3881020Smax.romanov@nginx.com     napi_value raw_headers, uint32_t idx)
389802Salexander.borisov@nginx.com {
390*1038Smax.romanov@nginx.com     char        *name;
391*1038Smax.romanov@nginx.com     uint8_t     i;
392*1038Smax.romanov@nginx.com     napi_value  str, vstr;
393*1038Smax.romanov@nginx.com 
394*1038Smax.romanov@nginx.com     name = (char *) nxt_unit_sptr_get(&f->name);
395802Salexander.borisov@nginx.com 
396*1038Smax.romanov@nginx.com     str = create_string_latin1(name, f->name_length);
397*1038Smax.romanov@nginx.com 
398*1038Smax.romanov@nginx.com     for (i = 0; i < f->name_length; i++) {
399*1038Smax.romanov@nginx.com         name[i] = lowcase(name[i]);
400*1038Smax.romanov@nginx.com     }
401802Salexander.borisov@nginx.com 
4021020Smax.romanov@nginx.com     vstr = set_named_property(headers, name, f->value, f->value_length);
403802Salexander.borisov@nginx.com 
4041020Smax.romanov@nginx.com     set_element(raw_headers, idx * 2, str);
4051020Smax.romanov@nginx.com     set_element(raw_headers, idx * 2 + 1, vstr);
406802Salexander.borisov@nginx.com }
407802Salexander.borisov@nginx.com 
408802Salexander.borisov@nginx.com 
409802Salexander.borisov@nginx.com napi_value
410802Salexander.borisov@nginx.com Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req)
411802Salexander.borisov@nginx.com {
4121022Smax.romanov@nginx.com     napi_value          constructor, res;
4131022Smax.romanov@nginx.com     nxt_unit_request_t  *r;
4141022Smax.romanov@nginx.com 
4151022Smax.romanov@nginx.com     r = req->request;
416802Salexander.borisov@nginx.com 
4171020Smax.romanov@nginx.com     constructor = get_named_property(server_obj, "socket");
418802Salexander.borisov@nginx.com 
4191022Smax.romanov@nginx.com     res = new_instance(constructor);
420802Salexander.borisov@nginx.com 
4211022Smax.romanov@nginx.com     set_named_property(res, "req_pointer", (intptr_t) req);
4221022Smax.romanov@nginx.com     set_named_property(res, "remoteAddress", r->remote, r->remote_length);
4231022Smax.romanov@nginx.com     set_named_property(res, "localAddress", r->local, r->local_length);
424828Salexander.borisov@nginx.com 
4251022Smax.romanov@nginx.com     return res;
426802Salexander.borisov@nginx.com }
427802Salexander.borisov@nginx.com 
428802Salexander.borisov@nginx.com 
429802Salexander.borisov@nginx.com napi_value
430802Salexander.borisov@nginx.com Unit::create_request(napi_value server_obj, napi_value socket)
431802Salexander.borisov@nginx.com {
4321020Smax.romanov@nginx.com     napi_value  constructor, return_val;
433802Salexander.borisov@nginx.com 
4341020Smax.romanov@nginx.com     constructor = get_named_property(server_obj, "request");
435802Salexander.borisov@nginx.com 
4361020Smax.romanov@nginx.com     return_val = new_instance(constructor, server_obj);
437802Salexander.borisov@nginx.com 
4381020Smax.romanov@nginx.com     set_named_property(return_val, "socket", socket);
4391022Smax.romanov@nginx.com     set_named_property(return_val, "connection", socket);
440802Salexander.borisov@nginx.com 
441802Salexander.borisov@nginx.com     return return_val;
442802Salexander.borisov@nginx.com }
443802Salexander.borisov@nginx.com 
444802Salexander.borisov@nginx.com 
445802Salexander.borisov@nginx.com napi_value
446802Salexander.borisov@nginx.com Unit::create_response(napi_value server_obj, napi_value socket,
4471020Smax.romanov@nginx.com     napi_value request, nxt_unit_request_info_t *req)
448802Salexander.borisov@nginx.com {
4491020Smax.romanov@nginx.com     napi_value  constructor, return_val;
450802Salexander.borisov@nginx.com 
4511020Smax.romanov@nginx.com     constructor = get_named_property(server_obj, "response");
452802Salexander.borisov@nginx.com 
4531020Smax.romanov@nginx.com     return_val = new_instance(constructor, request);
454802Salexander.borisov@nginx.com 
4551020Smax.romanov@nginx.com     set_named_property(return_val, "socket", socket);
4561022Smax.romanov@nginx.com     set_named_property(return_val, "connection", socket);
4571020Smax.romanov@nginx.com     set_named_property(return_val, "_req_point", (intptr_t) req);
458802Salexander.borisov@nginx.com 
459802Salexander.borisov@nginx.com     return return_val;
460802Salexander.borisov@nginx.com }
461802Salexander.borisov@nginx.com 
462802Salexander.borisov@nginx.com 
463802Salexander.borisov@nginx.com napi_value
464802Salexander.borisov@nginx.com Unit::response_send_headers(napi_env env, napi_callback_info info)
465802Salexander.borisov@nginx.com {
466802Salexander.borisov@nginx.com     int                      ret;
467802Salexander.borisov@nginx.com     char                     *ptr, *name_ptr;
468802Salexander.borisov@nginx.com     bool                     is_array;
469802Salexander.borisov@nginx.com     size_t                   argc, name_len, value_len;
470802Salexander.borisov@nginx.com     uint32_t                 status_code, header_len, keys_len, array_len;
471802Salexander.borisov@nginx.com     uint32_t                 keys_count, i, j;
472802Salexander.borisov@nginx.com     uint16_t                 hash;
4731020Smax.romanov@nginx.com     nxt_napi                 napi(env);
474802Salexander.borisov@nginx.com     napi_value               this_arg, headers, keys, name, value, array_val;
475875Salexander.borisov@nginx.com     napi_value               req_num, array_entry;
476843Salexander.borisov@nginx.com     napi_valuetype           val_type;
477802Salexander.borisov@nginx.com     nxt_unit_field_t         *f;
478802Salexander.borisov@nginx.com     nxt_unit_request_info_t  *req;
479802Salexander.borisov@nginx.com     napi_value               argv[5];
480802Salexander.borisov@nginx.com 
481802Salexander.borisov@nginx.com     argc = 5;
482802Salexander.borisov@nginx.com 
4831020Smax.romanov@nginx.com     try {
4841020Smax.romanov@nginx.com         this_arg = napi.get_cb_info(info, argc, argv);
4851020Smax.romanov@nginx.com         if (argc != 5) {
4861020Smax.romanov@nginx.com             napi.throw_error("Wrong args count. Expected: "
4871020Smax.romanov@nginx.com                              "statusCode, headers, headers count, "
4881020Smax.romanov@nginx.com                              "headers length");
4891020Smax.romanov@nginx.com             return nullptr;
4901020Smax.romanov@nginx.com         }
491802Salexander.borisov@nginx.com 
4921020Smax.romanov@nginx.com         req_num = napi.get_named_property(argv[0], "_req_point");
493802Salexander.borisov@nginx.com 
4941020Smax.romanov@nginx.com         req = napi.get_request_info(req_num);
495802Salexander.borisov@nginx.com 
4961020Smax.romanov@nginx.com         status_code = napi.get_value_uint32(argv[1]);
4971020Smax.romanov@nginx.com         keys_count = napi.get_value_uint32(argv[3]);
4981020Smax.romanov@nginx.com         header_len = napi.get_value_uint32(argv[4]);
499802Salexander.borisov@nginx.com 
5001020Smax.romanov@nginx.com         /* Need to reserve extra byte for C-string 0-termination. */
5011020Smax.romanov@nginx.com         header_len++;
502802Salexander.borisov@nginx.com 
5031020Smax.romanov@nginx.com         headers = argv[2];
504802Salexander.borisov@nginx.com 
5051020Smax.romanov@nginx.com         ret = nxt_unit_response_init(req, status_code, keys_count, header_len);
5061020Smax.romanov@nginx.com         if (ret != NXT_UNIT_OK) {
5071020Smax.romanov@nginx.com             napi.throw_error("Failed to create response");
5081020Smax.romanov@nginx.com             return nullptr;
509802Salexander.borisov@nginx.com         }
510802Salexander.borisov@nginx.com 
5111020Smax.romanov@nginx.com         keys = napi.get_property_names(headers);
5121020Smax.romanov@nginx.com         keys_len = napi.get_array_length(keys);
5131020Smax.romanov@nginx.com 
5141020Smax.romanov@nginx.com         ptr = req->response_buf->free;
515875Salexander.borisov@nginx.com 
5161020Smax.romanov@nginx.com         for (i = 0; i < keys_len; i++) {
5171020Smax.romanov@nginx.com             name = napi.get_element(keys, i);
5181020Smax.romanov@nginx.com 
5191020Smax.romanov@nginx.com             array_entry = napi.get_property(headers, name);
5201020Smax.romanov@nginx.com 
5211020Smax.romanov@nginx.com             name = napi.get_element(array_entry, 0);
5221020Smax.romanov@nginx.com             value = napi.get_element(array_entry, 1);
523875Salexander.borisov@nginx.com 
5241020Smax.romanov@nginx.com             name_len = napi.get_value_string_latin1(name, ptr, header_len);
5251020Smax.romanov@nginx.com             name_ptr = ptr;
5261020Smax.romanov@nginx.com 
5271020Smax.romanov@nginx.com             ptr += name_len;
5281020Smax.romanov@nginx.com             header_len -= name_len;
529802Salexander.borisov@nginx.com 
5301020Smax.romanov@nginx.com             hash = nxt_unit_field_hash(name_ptr, name_len);
5311020Smax.romanov@nginx.com 
5321020Smax.romanov@nginx.com             is_array = napi.is_array(value);
5331020Smax.romanov@nginx.com 
5341020Smax.romanov@nginx.com             if (is_array) {
5351020Smax.romanov@nginx.com                 array_len = napi.get_array_length(value);
536802Salexander.borisov@nginx.com 
5371020Smax.romanov@nginx.com                 for (j = 0; j < array_len; j++) {
5381020Smax.romanov@nginx.com                     array_val = napi.get_element(value, j);
5391020Smax.romanov@nginx.com 
5401020Smax.romanov@nginx.com                     val_type = napi.type_of(array_val);
541802Salexander.borisov@nginx.com 
5421020Smax.romanov@nginx.com                     if (val_type != napi_string) {
5431020Smax.romanov@nginx.com                         array_val = napi.coerce_to_string(array_val);
5441020Smax.romanov@nginx.com                     }
545802Salexander.borisov@nginx.com 
5461020Smax.romanov@nginx.com                     value_len = napi.get_value_string_latin1(array_val, ptr,
5471020Smax.romanov@nginx.com                                                              header_len);
548802Salexander.borisov@nginx.com 
5491020Smax.romanov@nginx.com                     f = req->response->fields + req->response->fields_count;
5501020Smax.romanov@nginx.com                     f->skip = 0;
5511020Smax.romanov@nginx.com 
5521020Smax.romanov@nginx.com                     nxt_unit_sptr_set(&f->name, name_ptr);
5531020Smax.romanov@nginx.com 
5541020Smax.romanov@nginx.com                     f->name_length = name_len;
5551020Smax.romanov@nginx.com                     f->hash = hash;
556802Salexander.borisov@nginx.com 
5571020Smax.romanov@nginx.com                     nxt_unit_sptr_set(&f->value, ptr);
5581020Smax.romanov@nginx.com                     f->value_length = (uint32_t) value_len;
559802Salexander.borisov@nginx.com 
5601020Smax.romanov@nginx.com                     ptr += value_len;
5611020Smax.romanov@nginx.com                     header_len -= value_len;
5621020Smax.romanov@nginx.com 
5631020Smax.romanov@nginx.com                     req->response->fields_count++;
564802Salexander.borisov@nginx.com                 }
565802Salexander.borisov@nginx.com 
5661020Smax.romanov@nginx.com             } else {
5671020Smax.romanov@nginx.com                 val_type = napi.type_of(value);
568843Salexander.borisov@nginx.com 
569843Salexander.borisov@nginx.com                 if (val_type != napi_string) {
5701020Smax.romanov@nginx.com                     value = napi.coerce_to_string(value);
571843Salexander.borisov@nginx.com                 }
572843Salexander.borisov@nginx.com 
5731020Smax.romanov@nginx.com                 value_len = napi.get_value_string_latin1(value, ptr, header_len);
574802Salexander.borisov@nginx.com 
575802Salexander.borisov@nginx.com                 f = req->response->fields + req->response->fields_count;
576802Salexander.borisov@nginx.com                 f->skip = 0;
577802Salexander.borisov@nginx.com 
578802Salexander.borisov@nginx.com                 nxt_unit_sptr_set(&f->name, name_ptr);
579802Salexander.borisov@nginx.com 
580802Salexander.borisov@nginx.com                 f->name_length = name_len;
581802Salexander.borisov@nginx.com                 f->hash = hash;
582802Salexander.borisov@nginx.com 
583802Salexander.borisov@nginx.com                 nxt_unit_sptr_set(&f->value, ptr);
584802Salexander.borisov@nginx.com                 f->value_length = (uint32_t) value_len;
585802Salexander.borisov@nginx.com 
586802Salexander.borisov@nginx.com                 ptr += value_len;
587802Salexander.borisov@nginx.com                 header_len -= value_len;
588802Salexander.borisov@nginx.com 
589802Salexander.borisov@nginx.com                 req->response->fields_count++;
590802Salexander.borisov@nginx.com             }
5911020Smax.romanov@nginx.com         }
592843Salexander.borisov@nginx.com 
5931020Smax.romanov@nginx.com     } catch (exception &e) {
5941020Smax.romanov@nginx.com         napi.throw_error(e);
5951020Smax.romanov@nginx.com         return nullptr;
596802Salexander.borisov@nginx.com     }
597802Salexander.borisov@nginx.com 
598802Salexander.borisov@nginx.com     req->response_buf->free = ptr;
599802Salexander.borisov@nginx.com 
600802Salexander.borisov@nginx.com     ret = nxt_unit_response_send(req);
601802Salexander.borisov@nginx.com     if (ret != NXT_UNIT_OK) {
6021020Smax.romanov@nginx.com         napi.throw_error("Failed to send response");
6031020Smax.romanov@nginx.com         return nullptr;
604802Salexander.borisov@nginx.com     }
605802Salexander.borisov@nginx.com 
606802Salexander.borisov@nginx.com     return this_arg;
607802Salexander.borisov@nginx.com }
608802Salexander.borisov@nginx.com 
609802Salexander.borisov@nginx.com 
610802Salexander.borisov@nginx.com napi_value
611802Salexander.borisov@nginx.com Unit::response_write(napi_env env, napi_callback_info info)
612802Salexander.borisov@nginx.com {
613802Salexander.borisov@nginx.com     int                      ret;
614802Salexander.borisov@nginx.com     char                     *ptr;
615802Salexander.borisov@nginx.com     size_t                   argc, have_buf_len;
616802Salexander.borisov@nginx.com     uint32_t                 buf_len;
6171020Smax.romanov@nginx.com     nxt_napi                 napi(env);
618802Salexander.borisov@nginx.com     napi_value               this_arg, req_num;
619802Salexander.borisov@nginx.com     napi_status              status;
620802Salexander.borisov@nginx.com     nxt_unit_buf_t           *buf;
621802Salexander.borisov@nginx.com     napi_valuetype           buf_type;
622802Salexander.borisov@nginx.com     nxt_unit_request_info_t  *req;
623802Salexander.borisov@nginx.com     napi_value               argv[3];
624802Salexander.borisov@nginx.com 
625802Salexander.borisov@nginx.com     argc = 3;
626802Salexander.borisov@nginx.com 
6271020Smax.romanov@nginx.com     try {
6281020Smax.romanov@nginx.com         this_arg = napi.get_cb_info(info, argc, argv);
6291020Smax.romanov@nginx.com         if (argc != 3) {
6301020Smax.romanov@nginx.com             throw exception("Wrong args count. Expected: "
6311020Smax.romanov@nginx.com                             "chunk, chunk length");
6321020Smax.romanov@nginx.com         }
633802Salexander.borisov@nginx.com 
6341020Smax.romanov@nginx.com         req_num = napi.get_named_property(argv[0], "_req_point");
6351020Smax.romanov@nginx.com         req = napi.get_request_info(req_num);
636802Salexander.borisov@nginx.com 
6371020Smax.romanov@nginx.com         buf_len = napi.get_value_uint32(argv[2]);
638802Salexander.borisov@nginx.com 
6391020Smax.romanov@nginx.com         buf_type = napi.type_of(argv[1]);
640802Salexander.borisov@nginx.com 
6411020Smax.romanov@nginx.com     } catch (exception &e) {
6421020Smax.romanov@nginx.com         napi.throw_error(e);
6431020Smax.romanov@nginx.com         return nullptr;
644802Salexander.borisov@nginx.com     }
645802Salexander.borisov@nginx.com 
646802Salexander.borisov@nginx.com     buf_len++;
647802Salexander.borisov@nginx.com 
648802Salexander.borisov@nginx.com     buf = nxt_unit_response_buf_alloc(req, buf_len);
649802Salexander.borisov@nginx.com     if (buf == NULL) {
650802Salexander.borisov@nginx.com         goto failed;
651802Salexander.borisov@nginx.com     }
652802Salexander.borisov@nginx.com 
653802Salexander.borisov@nginx.com     if (buf_type == napi_string) {
654802Salexander.borisov@nginx.com         /* TODO: will work only for utf8 content-type */
655802Salexander.borisov@nginx.com 
656802Salexander.borisov@nginx.com         status = napi_get_value_string_utf8(env, argv[1], buf->free,
657802Salexander.borisov@nginx.com                                             buf_len, &have_buf_len);
658802Salexander.borisov@nginx.com 
659802Salexander.borisov@nginx.com     } else {
660802Salexander.borisov@nginx.com         status = napi_get_buffer_info(env, argv[1], (void **) &ptr,
661802Salexander.borisov@nginx.com                                       &have_buf_len);
662802Salexander.borisov@nginx.com 
663802Salexander.borisov@nginx.com         memcpy(buf->free, ptr, have_buf_len);
664802Salexander.borisov@nginx.com     }
665802Salexander.borisov@nginx.com 
666802Salexander.borisov@nginx.com     if (status != napi_ok) {
667802Salexander.borisov@nginx.com         goto failed;
668802Salexander.borisov@nginx.com     }
669802Salexander.borisov@nginx.com 
670802Salexander.borisov@nginx.com     buf->free += have_buf_len;
671802Salexander.borisov@nginx.com 
672802Salexander.borisov@nginx.com     ret = nxt_unit_buf_send(buf);
673802Salexander.borisov@nginx.com     if (ret != NXT_UNIT_OK) {
674802Salexander.borisov@nginx.com         goto failed;
675802Salexander.borisov@nginx.com     }
676802Salexander.borisov@nginx.com 
677802Salexander.borisov@nginx.com     return this_arg;
678802Salexander.borisov@nginx.com 
679802Salexander.borisov@nginx.com failed:
680802Salexander.borisov@nginx.com 
6811020Smax.romanov@nginx.com     napi.throw_error("Failed to write body");
682802Salexander.borisov@nginx.com 
683802Salexander.borisov@nginx.com     return nullptr;
684802Salexander.borisov@nginx.com }
685802Salexander.borisov@nginx.com 
686802Salexander.borisov@nginx.com 
687802Salexander.borisov@nginx.com napi_value
688802Salexander.borisov@nginx.com Unit::response_end(napi_env env, napi_callback_info info)
689802Salexander.borisov@nginx.com {
690802Salexander.borisov@nginx.com     size_t                   argc;
6911020Smax.romanov@nginx.com     nxt_napi                 napi(env);
692802Salexander.borisov@nginx.com     napi_value               resp, this_arg, req_num;
693802Salexander.borisov@nginx.com     nxt_unit_request_info_t  *req;
694802Salexander.borisov@nginx.com 
695802Salexander.borisov@nginx.com     argc = 1;
696802Salexander.borisov@nginx.com 
6971020Smax.romanov@nginx.com     try {
6981020Smax.romanov@nginx.com         this_arg = napi.get_cb_info(info, argc, &resp);
6991020Smax.romanov@nginx.com 
7001020Smax.romanov@nginx.com         req_num = napi.get_named_property(resp, "_req_point");
7011020Smax.romanov@nginx.com         req = napi.get_request_info(req_num);
7021020Smax.romanov@nginx.com 
7031020Smax.romanov@nginx.com     } catch (exception &e) {
7041020Smax.romanov@nginx.com         napi.throw_error(e);
705802Salexander.borisov@nginx.com         return nullptr;
706802Salexander.borisov@nginx.com     }
707802Salexander.borisov@nginx.com 
708802Salexander.borisov@nginx.com     nxt_unit_request_done(req, NXT_UNIT_OK);
709802Salexander.borisov@nginx.com 
710802Salexander.borisov@nginx.com     return this_arg;
711802Salexander.borisov@nginx.com }
712