xref: /unit/src/nodejs/unit-http/unit.cpp (revision 1322)
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 
131132Smax.romanov@nginx.com #include <nxt_unit_websocket.h>
141132Smax.romanov@nginx.com 
15802Salexander.borisov@nginx.com 
16802Salexander.borisov@nginx.com napi_ref Unit::constructor_;
17802Salexander.borisov@nginx.com 
18802Salexander.borisov@nginx.com 
19842Salexander.borisov@nginx.com struct nxt_nodejs_ctx_t {
20842Salexander.borisov@nginx.com     nxt_unit_port_id_t  port_id;
21842Salexander.borisov@nginx.com     uv_poll_t           poll;
22842Salexander.borisov@nginx.com };
23842Salexander.borisov@nginx.com 
24842Salexander.borisov@nginx.com 
251132Smax.romanov@nginx.com struct req_data_t {
261132Smax.romanov@nginx.com     napi_ref  sock_ref;
271132Smax.romanov@nginx.com     napi_ref  resp_ref;
281132Smax.romanov@nginx.com     napi_ref  conn_ref;
291132Smax.romanov@nginx.com };
301132Smax.romanov@nginx.com 
311132Smax.romanov@nginx.com 
321020Smax.romanov@nginx.com Unit::Unit(napi_env env, napi_value jsthis):
331020Smax.romanov@nginx.com     nxt_napi(env),
341020Smax.romanov@nginx.com     wrapper_(wrap(jsthis, this, destroy)),
35802Salexander.borisov@nginx.com     unit_ctx_(nullptr)
36802Salexander.borisov@nginx.com {
371132Smax.romanov@nginx.com     nxt_unit_debug(NULL, "Unit::Unit()");
38802Salexander.borisov@nginx.com }
39802Salexander.borisov@nginx.com 
40802Salexander.borisov@nginx.com 
41802Salexander.borisov@nginx.com Unit::~Unit()
42802Salexander.borisov@nginx.com {
431020Smax.romanov@nginx.com     delete_reference(wrapper_);
441132Smax.romanov@nginx.com 
451132Smax.romanov@nginx.com     nxt_unit_debug(NULL, "Unit::~Unit()");
46802Salexander.borisov@nginx.com }
47802Salexander.borisov@nginx.com 
48802Salexander.borisov@nginx.com 
49802Salexander.borisov@nginx.com napi_value
50802Salexander.borisov@nginx.com Unit::init(napi_env env, napi_value exports)
51802Salexander.borisov@nginx.com {
521020Smax.romanov@nginx.com     nxt_napi    napi(env);
531132Smax.romanov@nginx.com     napi_value  ctor;
54802Salexander.borisov@nginx.com 
551132Smax.romanov@nginx.com     napi_property_descriptor  unit_props[] = {
56802Salexander.borisov@nginx.com         { "createServer", 0, create_server, 0, 0, 0, napi_default, 0 },
57828Salexander.borisov@nginx.com         { "listen", 0, listen, 0, 0, 0, napi_default, 0 },
58802Salexander.borisov@nginx.com     };
59802Salexander.borisov@nginx.com 
601020Smax.romanov@nginx.com     try {
611132Smax.romanov@nginx.com         ctor = napi.define_class("Unit", create, 2, unit_props);
621132Smax.romanov@nginx.com         constructor_ = napi.create_reference(ctor);
63802Salexander.borisov@nginx.com 
641132Smax.romanov@nginx.com         napi.set_named_property(exports, "Unit", ctor);
651132Smax.romanov@nginx.com         napi.set_named_property(exports, "response_send_headers",
661020Smax.romanov@nginx.com                                 response_send_headers);
671132Smax.romanov@nginx.com         napi.set_named_property(exports, "response_write", response_write);
681132Smax.romanov@nginx.com         napi.set_named_property(exports, "response_end", response_end);
691132Smax.romanov@nginx.com         napi.set_named_property(exports, "websocket_send_frame",
701132Smax.romanov@nginx.com                                 websocket_send_frame);
711132Smax.romanov@nginx.com         napi.set_named_property(exports, "websocket_set_sock",
721132Smax.romanov@nginx.com                                 websocket_set_sock);
73*1322Smax.romanov@nginx.com         napi.set_named_property(exports, "buf_min", nxt_unit_buf_min());
74*1322Smax.romanov@nginx.com         napi.set_named_property(exports, "buf_max", nxt_unit_buf_max());
75802Salexander.borisov@nginx.com 
761020Smax.romanov@nginx.com     } catch (exception &e) {
771020Smax.romanov@nginx.com         napi.throw_error(e);
781020Smax.romanov@nginx.com         return nullptr;
79802Salexander.borisov@nginx.com     }
80802Salexander.borisov@nginx.com 
81802Salexander.borisov@nginx.com     return exports;
82802Salexander.borisov@nginx.com }
83802Salexander.borisov@nginx.com 
84802Salexander.borisov@nginx.com 
85802Salexander.borisov@nginx.com void
86802Salexander.borisov@nginx.com Unit::destroy(napi_env env, void *nativeObject, void *finalize_hint)
87802Salexander.borisov@nginx.com {
88802Salexander.borisov@nginx.com     Unit  *obj = reinterpret_cast<Unit *>(nativeObject);
89802Salexander.borisov@nginx.com 
90802Salexander.borisov@nginx.com     delete obj;
91802Salexander.borisov@nginx.com }
92802Salexander.borisov@nginx.com 
93802Salexander.borisov@nginx.com 
94802Salexander.borisov@nginx.com napi_value
95802Salexander.borisov@nginx.com Unit::create(napi_env env, napi_callback_info info)
96802Salexander.borisov@nginx.com {
971020Smax.romanov@nginx.com     nxt_napi    napi(env);
981132Smax.romanov@nginx.com     napi_value  target, ctor, instance, jsthis;
991020Smax.romanov@nginx.com 
1001020Smax.romanov@nginx.com     try {
1011020Smax.romanov@nginx.com         target = napi.get_new_target(info);
102802Salexander.borisov@nginx.com 
1031020Smax.romanov@nginx.com         if (target != nullptr) {
1041020Smax.romanov@nginx.com             /* Invoked as constructor: `new Unit(...)`. */
1051020Smax.romanov@nginx.com             jsthis = napi.get_cb_info(info);
106802Salexander.borisov@nginx.com 
1071023Smax.romanov@nginx.com             new Unit(env, jsthis);
1081023Smax.romanov@nginx.com             napi.create_reference(jsthis);
1091020Smax.romanov@nginx.com 
1101020Smax.romanov@nginx.com             return jsthis;
111802Salexander.borisov@nginx.com         }
112802Salexander.borisov@nginx.com 
1131020Smax.romanov@nginx.com         /* Invoked as plain function `Unit(...)`, turn into construct call. */
1141132Smax.romanov@nginx.com         ctor = napi.get_reference_value(constructor_);
1151132Smax.romanov@nginx.com         instance = napi.new_instance(ctor);
1161023Smax.romanov@nginx.com         napi.create_reference(instance);
117841Salexander.borisov@nginx.com 
1181020Smax.romanov@nginx.com     } catch (exception &e) {
1191020Smax.romanov@nginx.com         napi.throw_error(e);
1201020Smax.romanov@nginx.com         return nullptr;
121841Salexander.borisov@nginx.com     }
122841Salexander.borisov@nginx.com 
123802Salexander.borisov@nginx.com     return instance;
124802Salexander.borisov@nginx.com }
125802Salexander.borisov@nginx.com 
126802Salexander.borisov@nginx.com 
127802Salexander.borisov@nginx.com napi_value
128802Salexander.borisov@nginx.com Unit::create_server(napi_env env, napi_callback_info info)
129802Salexander.borisov@nginx.com {
130802Salexander.borisov@nginx.com     Unit             *obj;
131802Salexander.borisov@nginx.com     size_t           argc;
1321020Smax.romanov@nginx.com     nxt_napi         napi(env);
133828Salexander.borisov@nginx.com     napi_value       jsthis, argv;
134802Salexander.borisov@nginx.com     nxt_unit_init_t  unit_init;
135802Salexander.borisov@nginx.com 
136802Salexander.borisov@nginx.com     argc = 1;
137802Salexander.borisov@nginx.com 
1381020Smax.romanov@nginx.com     try {
1391020Smax.romanov@nginx.com         jsthis = napi.get_cb_info(info, argc, &argv);
1401020Smax.romanov@nginx.com         obj = (Unit *) napi.unwrap(jsthis);
141802Salexander.borisov@nginx.com 
1421020Smax.romanov@nginx.com     } catch (exception &e) {
1431020Smax.romanov@nginx.com         napi.throw_error(e);
1441020Smax.romanov@nginx.com         return nullptr;
145802Salexander.borisov@nginx.com     }
146802Salexander.borisov@nginx.com 
147802Salexander.borisov@nginx.com     memset(&unit_init, 0, sizeof(nxt_unit_init_t));
148802Salexander.borisov@nginx.com 
149802Salexander.borisov@nginx.com     unit_init.data = obj;
1501132Smax.romanov@nginx.com     unit_init.callbacks.request_handler   = request_handler_cb;
1511132Smax.romanov@nginx.com     unit_init.callbacks.websocket_handler = websocket_handler_cb;
1521132Smax.romanov@nginx.com     unit_init.callbacks.close_handler     = close_handler_cb;
153*1322Smax.romanov@nginx.com     unit_init.callbacks.shm_ack_handler   = shm_ack_handler_cb;
1541132Smax.romanov@nginx.com     unit_init.callbacks.add_port          = add_port;
1551132Smax.romanov@nginx.com     unit_init.callbacks.remove_port       = remove_port;
1561132Smax.romanov@nginx.com     unit_init.callbacks.quit              = quit_cb;
1571132Smax.romanov@nginx.com 
1581132Smax.romanov@nginx.com     unit_init.request_data_size = sizeof(req_data_t);
159802Salexander.borisov@nginx.com 
160802Salexander.borisov@nginx.com     obj->unit_ctx_ = nxt_unit_init(&unit_init);
161802Salexander.borisov@nginx.com     if (obj->unit_ctx_ == NULL) {
162802Salexander.borisov@nginx.com         goto failed;
163802Salexander.borisov@nginx.com     }
164802Salexander.borisov@nginx.com 
165802Salexander.borisov@nginx.com     return nullptr;
166802Salexander.borisov@nginx.com 
167802Salexander.borisov@nginx.com failed:
168802Salexander.borisov@nginx.com 
169802Salexander.borisov@nginx.com     napi_throw_error(env, NULL, "Failed to create Unit object");
170802Salexander.borisov@nginx.com 
171802Salexander.borisov@nginx.com     return nullptr;
172802Salexander.borisov@nginx.com }
173802Salexander.borisov@nginx.com 
174802Salexander.borisov@nginx.com 
175802Salexander.borisov@nginx.com napi_value
176802Salexander.borisov@nginx.com Unit::listen(napi_env env, napi_callback_info info)
177802Salexander.borisov@nginx.com {
178828Salexander.borisov@nginx.com     return nullptr;
179828Salexander.borisov@nginx.com }
180828Salexander.borisov@nginx.com 
181802Salexander.borisov@nginx.com 
1821132Smax.romanov@nginx.com void
1831132Smax.romanov@nginx.com Unit::request_handler_cb(nxt_unit_request_info_t *req)
184828Salexander.borisov@nginx.com {
1851132Smax.romanov@nginx.com     Unit  *obj;
186828Salexander.borisov@nginx.com 
1871132Smax.romanov@nginx.com     obj = reinterpret_cast<Unit *>(req->unit->data);
188802Salexander.borisov@nginx.com 
1891132Smax.romanov@nginx.com     obj->request_handler(req);
190802Salexander.borisov@nginx.com }
191802Salexander.borisov@nginx.com 
192802Salexander.borisov@nginx.com 
193802Salexander.borisov@nginx.com void
194802Salexander.borisov@nginx.com Unit::request_handler(nxt_unit_request_info_t *req)
195802Salexander.borisov@nginx.com {
1961132Smax.romanov@nginx.com     napi_value  socket, request, response, server_obj, emit_request;
197802Salexander.borisov@nginx.com 
1981132Smax.romanov@nginx.com     memset(req->data, 0, sizeof(req_data_t));
199802Salexander.borisov@nginx.com 
2001020Smax.romanov@nginx.com     try {
2011132Smax.romanov@nginx.com         nxt_handle_scope  scope(env());
202802Salexander.borisov@nginx.com 
2031132Smax.romanov@nginx.com         server_obj = get_server_object();
204802Salexander.borisov@nginx.com 
2051132Smax.romanov@nginx.com         socket = create_socket(server_obj, req);
2061132Smax.romanov@nginx.com         request = create_request(server_obj, socket);
2071132Smax.romanov@nginx.com         response = create_response(server_obj, request, req);
208828Salexander.borisov@nginx.com 
2091132Smax.romanov@nginx.com         create_headers(req, request);
210871Salexander.borisov@nginx.com 
2111132Smax.romanov@nginx.com         emit_request = get_named_property(server_obj, "emit_request");
2121132Smax.romanov@nginx.com 
2131132Smax.romanov@nginx.com         nxt_async_context   async_context(env(), "request_handler");
2141020Smax.romanov@nginx.com         nxt_callback_scope  async_scope(async_context);
215876Salexander.borisov@nginx.com 
2161132Smax.romanov@nginx.com         make_callback(async_context, server_obj, emit_request, request,
2171132Smax.romanov@nginx.com                       response);
218871Salexander.borisov@nginx.com 
2191020Smax.romanov@nginx.com     } catch (exception &e) {
2201132Smax.romanov@nginx.com         nxt_unit_req_warn(req, "request_handler: %s", e.str);
221871Salexander.borisov@nginx.com     }
222802Salexander.borisov@nginx.com }
223802Salexander.borisov@nginx.com 
224802Salexander.borisov@nginx.com 
225828Salexander.borisov@nginx.com void
2261132Smax.romanov@nginx.com Unit::websocket_handler_cb(nxt_unit_websocket_frame_t *ws)
2271132Smax.romanov@nginx.com {
2281132Smax.romanov@nginx.com     Unit  *obj;
2291132Smax.romanov@nginx.com 
2301132Smax.romanov@nginx.com     obj = reinterpret_cast<Unit *>(ws->req->unit->data);
2311132Smax.romanov@nginx.com 
2321132Smax.romanov@nginx.com     obj->websocket_handler(ws);
2331132Smax.romanov@nginx.com }
2341132Smax.romanov@nginx.com 
2351132Smax.romanov@nginx.com 
2361132Smax.romanov@nginx.com void
2371132Smax.romanov@nginx.com Unit::websocket_handler(nxt_unit_websocket_frame_t *ws)
2381132Smax.romanov@nginx.com {
2391132Smax.romanov@nginx.com     napi_value  frame, server_obj, process_frame, conn;
2401132Smax.romanov@nginx.com     req_data_t  *req_data;
2411132Smax.romanov@nginx.com 
2421132Smax.romanov@nginx.com     req_data = (req_data_t *) ws->req->data;
2431132Smax.romanov@nginx.com 
2441132Smax.romanov@nginx.com     try {
2451132Smax.romanov@nginx.com         nxt_handle_scope  scope(env());
2461132Smax.romanov@nginx.com 
2471132Smax.romanov@nginx.com         server_obj = get_server_object();
2481132Smax.romanov@nginx.com 
2491132Smax.romanov@nginx.com         frame = create_websocket_frame(server_obj, ws);
2501132Smax.romanov@nginx.com 
2511132Smax.romanov@nginx.com         conn = get_reference_value(req_data->conn_ref);
2521132Smax.romanov@nginx.com 
2531132Smax.romanov@nginx.com         process_frame = get_named_property(conn, "processFrame");
2541132Smax.romanov@nginx.com 
2551132Smax.romanov@nginx.com         nxt_async_context   async_context(env(), "websocket_handler");
2561132Smax.romanov@nginx.com         nxt_callback_scope  async_scope(async_context);
2571132Smax.romanov@nginx.com 
2581132Smax.romanov@nginx.com         make_callback(async_context, conn, process_frame, frame);
2591132Smax.romanov@nginx.com 
2601132Smax.romanov@nginx.com     } catch (exception &e) {
2611132Smax.romanov@nginx.com         nxt_unit_req_warn(ws->req, "websocket_handler: %s", e.str);
2621132Smax.romanov@nginx.com     }
2631132Smax.romanov@nginx.com 
2641132Smax.romanov@nginx.com     nxt_unit_websocket_done(ws);
2651132Smax.romanov@nginx.com }
2661132Smax.romanov@nginx.com 
2671132Smax.romanov@nginx.com 
2681132Smax.romanov@nginx.com void
2691132Smax.romanov@nginx.com Unit::close_handler_cb(nxt_unit_request_info_t *req)
2701132Smax.romanov@nginx.com {
2711132Smax.romanov@nginx.com     Unit  *obj;
2721132Smax.romanov@nginx.com 
2731132Smax.romanov@nginx.com     obj = reinterpret_cast<Unit *>(req->unit->data);
2741132Smax.romanov@nginx.com 
2751132Smax.romanov@nginx.com     obj->close_handler(req);
2761132Smax.romanov@nginx.com }
2771132Smax.romanov@nginx.com 
2781132Smax.romanov@nginx.com 
2791132Smax.romanov@nginx.com void
2801132Smax.romanov@nginx.com Unit::close_handler(nxt_unit_request_info_t *req)
2811132Smax.romanov@nginx.com {
2821132Smax.romanov@nginx.com     napi_value  conn_handle_close, conn;
2831132Smax.romanov@nginx.com     req_data_t  *req_data;
2841132Smax.romanov@nginx.com 
2851132Smax.romanov@nginx.com     req_data = (req_data_t *) req->data;
2861132Smax.romanov@nginx.com 
2871132Smax.romanov@nginx.com     try {
2881132Smax.romanov@nginx.com         nxt_handle_scope  scope(env());
2891132Smax.romanov@nginx.com 
2901132Smax.romanov@nginx.com         conn = get_reference_value(req_data->conn_ref);
2911132Smax.romanov@nginx.com 
2921132Smax.romanov@nginx.com         conn_handle_close = get_named_property(conn, "handleSocketClose");
2931132Smax.romanov@nginx.com 
2941132Smax.romanov@nginx.com         nxt_async_context   async_context(env(), "close_handler");
2951132Smax.romanov@nginx.com         nxt_callback_scope  async_scope(async_context);
2961132Smax.romanov@nginx.com 
2971132Smax.romanov@nginx.com         make_callback(async_context, conn, conn_handle_close,
2981132Smax.romanov@nginx.com                       nxt_napi::create(0));
2991132Smax.romanov@nginx.com 
3001132Smax.romanov@nginx.com         remove_wrap(req_data->sock_ref);
3011132Smax.romanov@nginx.com         remove_wrap(req_data->resp_ref);
3021132Smax.romanov@nginx.com         remove_wrap(req_data->conn_ref);
3031132Smax.romanov@nginx.com 
3041132Smax.romanov@nginx.com     } catch (exception &e) {
3051132Smax.romanov@nginx.com         nxt_unit_req_warn(req, "close_handler: %s", e.str);
3061132Smax.romanov@nginx.com 
3071132Smax.romanov@nginx.com         return;
3081132Smax.romanov@nginx.com     }
3091132Smax.romanov@nginx.com 
3101132Smax.romanov@nginx.com     nxt_unit_request_done(req, NXT_UNIT_OK);
3111132Smax.romanov@nginx.com }
3121132Smax.romanov@nginx.com 
3131132Smax.romanov@nginx.com 
314*1322Smax.romanov@nginx.com void
315*1322Smax.romanov@nginx.com Unit::shm_ack_handler_cb(nxt_unit_ctx_t *ctx)
316*1322Smax.romanov@nginx.com {
317*1322Smax.romanov@nginx.com     Unit  *obj;
318*1322Smax.romanov@nginx.com 
319*1322Smax.romanov@nginx.com     obj = reinterpret_cast<Unit *>(ctx->unit->data);
320*1322Smax.romanov@nginx.com 
321*1322Smax.romanov@nginx.com     obj->shm_ack_handler(ctx);
322*1322Smax.romanov@nginx.com }
323*1322Smax.romanov@nginx.com 
324*1322Smax.romanov@nginx.com 
325*1322Smax.romanov@nginx.com void
326*1322Smax.romanov@nginx.com Unit::shm_ack_handler(nxt_unit_ctx_t *ctx)
327*1322Smax.romanov@nginx.com {
328*1322Smax.romanov@nginx.com     napi_value  server_obj, emit_drain;
329*1322Smax.romanov@nginx.com 
330*1322Smax.romanov@nginx.com     try {
331*1322Smax.romanov@nginx.com         nxt_handle_scope  scope(env());
332*1322Smax.romanov@nginx.com 
333*1322Smax.romanov@nginx.com         server_obj = get_server_object();
334*1322Smax.romanov@nginx.com 
335*1322Smax.romanov@nginx.com         emit_drain = get_named_property(server_obj, "emit_drain");
336*1322Smax.romanov@nginx.com 
337*1322Smax.romanov@nginx.com         nxt_async_context   async_context(env(), "shm_ack_handler");
338*1322Smax.romanov@nginx.com         nxt_callback_scope  async_scope(async_context);
339*1322Smax.romanov@nginx.com 
340*1322Smax.romanov@nginx.com         make_callback(async_context, server_obj, emit_drain);
341*1322Smax.romanov@nginx.com 
342*1322Smax.romanov@nginx.com     } catch (exception &e) {
343*1322Smax.romanov@nginx.com         nxt_unit_warn(ctx, "shm_ack_handler: %s", e.str);
344*1322Smax.romanov@nginx.com     }
345*1322Smax.romanov@nginx.com }
346*1322Smax.romanov@nginx.com 
347*1322Smax.romanov@nginx.com 
3481132Smax.romanov@nginx.com static void
349828Salexander.borisov@nginx.com nxt_uv_read_callback(uv_poll_t *handle, int status, int events)
350828Salexander.borisov@nginx.com {
351828Salexander.borisov@nginx.com     nxt_unit_run_once((nxt_unit_ctx_t *) handle->data);
352828Salexander.borisov@nginx.com }
353828Salexander.borisov@nginx.com 
354828Salexander.borisov@nginx.com 
355828Salexander.borisov@nginx.com int
356828Salexander.borisov@nginx.com Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
357828Salexander.borisov@nginx.com {
358842Salexander.borisov@nginx.com     int               err;
359842Salexander.borisov@nginx.com     Unit              *obj;
360842Salexander.borisov@nginx.com     uv_loop_t         *loop;
361842Salexander.borisov@nginx.com     napi_status       status;
362842Salexander.borisov@nginx.com     nxt_nodejs_ctx_t  *node_ctx;
363828Salexander.borisov@nginx.com 
364828Salexander.borisov@nginx.com     if (port->in_fd != -1) {
365828Salexander.borisov@nginx.com         obj = reinterpret_cast<Unit *>(ctx->unit->data);
366828Salexander.borisov@nginx.com 
367828Salexander.borisov@nginx.com         if (fcntl(port->in_fd, F_SETFL, O_NONBLOCK) == -1) {
3681132Smax.romanov@nginx.com             nxt_unit_warn(ctx, "fcntl(%d, O_NONBLOCK) failed: %s (%d)",
3691132Smax.romanov@nginx.com                           port->in_fd, strerror(errno), errno);
370828Salexander.borisov@nginx.com             return -1;
371828Salexander.borisov@nginx.com         }
372828Salexander.borisov@nginx.com 
3731020Smax.romanov@nginx.com         status = napi_get_uv_event_loop(obj->env(), &loop);
374828Salexander.borisov@nginx.com         if (status != napi_ok) {
3751132Smax.romanov@nginx.com             nxt_unit_warn(ctx, "Failed to get uv.loop");
376828Salexander.borisov@nginx.com             return NXT_UNIT_ERROR;
377828Salexander.borisov@nginx.com         }
378828Salexander.borisov@nginx.com 
379842Salexander.borisov@nginx.com         node_ctx = new nxt_nodejs_ctx_t;
380828Salexander.borisov@nginx.com 
381842Salexander.borisov@nginx.com         err = uv_poll_init(loop, &node_ctx->poll, port->in_fd);
382828Salexander.borisov@nginx.com         if (err < 0) {
3831132Smax.romanov@nginx.com             nxt_unit_warn(ctx, "Failed to init uv.poll");
384828Salexander.borisov@nginx.com             return NXT_UNIT_ERROR;
385828Salexander.borisov@nginx.com         }
386828Salexander.borisov@nginx.com 
387842Salexander.borisov@nginx.com         err = uv_poll_start(&node_ctx->poll, UV_READABLE, nxt_uv_read_callback);
388828Salexander.borisov@nginx.com         if (err < 0) {
3891132Smax.romanov@nginx.com             nxt_unit_warn(ctx, "Failed to start uv.poll");
390828Salexander.borisov@nginx.com             return NXT_UNIT_ERROR;
391828Salexander.borisov@nginx.com         }
392828Salexander.borisov@nginx.com 
393842Salexander.borisov@nginx.com         ctx->data = node_ctx;
394842Salexander.borisov@nginx.com 
395842Salexander.borisov@nginx.com         node_ctx->port_id = port->id;
396842Salexander.borisov@nginx.com         node_ctx->poll.data = ctx;
397828Salexander.borisov@nginx.com     }
398828Salexander.borisov@nginx.com 
399828Salexander.borisov@nginx.com     return nxt_unit_add_port(ctx, port);
400828Salexander.borisov@nginx.com }
401828Salexander.borisov@nginx.com 
402828Salexander.borisov@nginx.com 
4031008Szelenkov@nginx.com inline bool
404842Salexander.borisov@nginx.com operator == (const nxt_unit_port_id_t &p1, const nxt_unit_port_id_t &p2)
405842Salexander.borisov@nginx.com {
406842Salexander.borisov@nginx.com     return p1.pid == p2.pid && p1.id == p2.id;
407842Salexander.borisov@nginx.com }
408842Salexander.borisov@nginx.com 
409842Salexander.borisov@nginx.com 
410828Salexander.borisov@nginx.com void
411828Salexander.borisov@nginx.com Unit::remove_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id)
412828Salexander.borisov@nginx.com {
413842Salexander.borisov@nginx.com     nxt_nodejs_ctx_t  *node_ctx;
414842Salexander.borisov@nginx.com 
415842Salexander.borisov@nginx.com     if (ctx->data != NULL) {
416842Salexander.borisov@nginx.com         node_ctx = (nxt_nodejs_ctx_t *) ctx->data;
417828Salexander.borisov@nginx.com 
418842Salexander.borisov@nginx.com         if (node_ctx->port_id == *port_id) {
419842Salexander.borisov@nginx.com             uv_poll_stop(&node_ctx->poll);
420828Salexander.borisov@nginx.com 
421842Salexander.borisov@nginx.com             delete node_ctx;
422828Salexander.borisov@nginx.com 
423842Salexander.borisov@nginx.com             ctx->data = NULL;
424842Salexander.borisov@nginx.com         }
425828Salexander.borisov@nginx.com     }
426828Salexander.borisov@nginx.com 
427828Salexander.borisov@nginx.com     nxt_unit_remove_port(ctx, port_id);
428828Salexander.borisov@nginx.com }
429828Salexander.borisov@nginx.com 
430828Salexander.borisov@nginx.com 
431842Salexander.borisov@nginx.com void
4321132Smax.romanov@nginx.com Unit::quit_cb(nxt_unit_ctx_t *ctx)
433842Salexander.borisov@nginx.com {
4341132Smax.romanov@nginx.com     Unit  *obj;
4351021Smax.romanov@nginx.com 
4361021Smax.romanov@nginx.com     obj = reinterpret_cast<Unit *>(ctx->unit->data);
4371021Smax.romanov@nginx.com 
4381132Smax.romanov@nginx.com     obj->quit(ctx);
4391132Smax.romanov@nginx.com }
4401132Smax.romanov@nginx.com 
4411132Smax.romanov@nginx.com 
4421132Smax.romanov@nginx.com void
4431132Smax.romanov@nginx.com Unit::quit(nxt_unit_ctx_t *ctx)
4441132Smax.romanov@nginx.com {
4451132Smax.romanov@nginx.com     napi_value  server_obj, emit_close;
4461021Smax.romanov@nginx.com 
4471132Smax.romanov@nginx.com     try {
4481132Smax.romanov@nginx.com         nxt_handle_scope  scope(env());
4491021Smax.romanov@nginx.com 
4501132Smax.romanov@nginx.com         server_obj = get_server_object();
4511021Smax.romanov@nginx.com 
4521132Smax.romanov@nginx.com         emit_close = get_named_property(server_obj, "emit_close");
4531132Smax.romanov@nginx.com 
4541132Smax.romanov@nginx.com         nxt_async_context   async_context(env(), "unit_quit");
4551021Smax.romanov@nginx.com         nxt_callback_scope  async_scope(async_context);
4561021Smax.romanov@nginx.com 
4571132Smax.romanov@nginx.com         make_callback(async_context, server_obj, emit_close);
4581021Smax.romanov@nginx.com 
4591021Smax.romanov@nginx.com     } catch (exception &e) {
4601132Smax.romanov@nginx.com         nxt_unit_debug(ctx, "quit: %s", e.str);
4611021Smax.romanov@nginx.com     }
4621021Smax.romanov@nginx.com 
463842Salexander.borisov@nginx.com     nxt_unit_done(ctx);
464842Salexander.borisov@nginx.com }
465842Salexander.borisov@nginx.com 
466842Salexander.borisov@nginx.com 
467802Salexander.borisov@nginx.com napi_value
468802Salexander.borisov@nginx.com Unit::get_server_object()
469802Salexander.borisov@nginx.com {
4701020Smax.romanov@nginx.com     napi_value  unit_obj;
471802Salexander.borisov@nginx.com 
4721020Smax.romanov@nginx.com     unit_obj = get_reference_value(wrapper_);
473802Salexander.borisov@nginx.com 
4741020Smax.romanov@nginx.com     return get_named_property(unit_obj, "server");
475802Salexander.borisov@nginx.com }
476802Salexander.borisov@nginx.com 
477802Salexander.borisov@nginx.com 
4781020Smax.romanov@nginx.com void
479802Salexander.borisov@nginx.com Unit::create_headers(nxt_unit_request_info_t *req, napi_value request)
480802Salexander.borisov@nginx.com {
4811132Smax.romanov@nginx.com     void                *data;
482802Salexander.borisov@nginx.com     uint32_t            i;
4831132Smax.romanov@nginx.com     napi_value          headers, raw_headers, buffer;
484802Salexander.borisov@nginx.com     napi_status         status;
485802Salexander.borisov@nginx.com     nxt_unit_request_t  *r;
486802Salexander.borisov@nginx.com 
487802Salexander.borisov@nginx.com     r = req->request;
488802Salexander.borisov@nginx.com 
4891020Smax.romanov@nginx.com     headers = create_object();
490802Salexander.borisov@nginx.com 
4911020Smax.romanov@nginx.com     status = napi_create_array_with_length(env(), r->fields_count * 2,
492802Salexander.borisov@nginx.com                                            &raw_headers);
493802Salexander.borisov@nginx.com     if (status != napi_ok) {
4941020Smax.romanov@nginx.com         throw exception("Failed to create array");
495802Salexander.borisov@nginx.com     }
496802Salexander.borisov@nginx.com 
497802Salexander.borisov@nginx.com     for (i = 0; i < r->fields_count; i++) {
4981020Smax.romanov@nginx.com         append_header(r->fields + i, headers, raw_headers, i);
499802Salexander.borisov@nginx.com     }
500802Salexander.borisov@nginx.com 
5011020Smax.romanov@nginx.com     set_named_property(request, "headers", headers);
5021020Smax.romanov@nginx.com     set_named_property(request, "rawHeaders", raw_headers);
5031020Smax.romanov@nginx.com     set_named_property(request, "httpVersion", r->version, r->version_length);
5041020Smax.romanov@nginx.com     set_named_property(request, "method", r->method, r->method_length);
5051020Smax.romanov@nginx.com     set_named_property(request, "url", r->target, r->target_length);
5061132Smax.romanov@nginx.com 
5071132Smax.romanov@nginx.com     set_named_property(request, "_websocket_handshake", r->websocket_handshake);
5081132Smax.romanov@nginx.com 
5091132Smax.romanov@nginx.com     buffer = create_buffer((size_t) req->content_length, &data);
5101132Smax.romanov@nginx.com     nxt_unit_request_read(req, data, req->content_length);
5111132Smax.romanov@nginx.com 
5121132Smax.romanov@nginx.com     set_named_property(request, "_data", buffer);
513802Salexander.borisov@nginx.com }
514802Salexander.borisov@nginx.com 
515802Salexander.borisov@nginx.com 
5161038Smax.romanov@nginx.com inline char
5171038Smax.romanov@nginx.com lowcase(char c)
5181038Smax.romanov@nginx.com {
5191038Smax.romanov@nginx.com     return (c >= 'A' && c <= 'Z') ? (c | 0x20) : c;
5201038Smax.romanov@nginx.com }
5211038Smax.romanov@nginx.com 
5221038Smax.romanov@nginx.com 
5231020Smax.romanov@nginx.com inline void
524802Salexander.borisov@nginx.com Unit::append_header(nxt_unit_field_t *f, napi_value headers,
5251020Smax.romanov@nginx.com     napi_value raw_headers, uint32_t idx)
526802Salexander.borisov@nginx.com {
5271038Smax.romanov@nginx.com     char        *name;
5281038Smax.romanov@nginx.com     uint8_t     i;
5291038Smax.romanov@nginx.com     napi_value  str, vstr;
5301038Smax.romanov@nginx.com 
5311038Smax.romanov@nginx.com     name = (char *) nxt_unit_sptr_get(&f->name);
532802Salexander.borisov@nginx.com 
5331038Smax.romanov@nginx.com     str = create_string_latin1(name, f->name_length);
5341038Smax.romanov@nginx.com 
5351038Smax.romanov@nginx.com     for (i = 0; i < f->name_length; i++) {
5361038Smax.romanov@nginx.com         name[i] = lowcase(name[i]);
5371038Smax.romanov@nginx.com     }
538802Salexander.borisov@nginx.com 
5391020Smax.romanov@nginx.com     vstr = set_named_property(headers, name, f->value, f->value_length);
540802Salexander.borisov@nginx.com 
5411020Smax.romanov@nginx.com     set_element(raw_headers, idx * 2, str);
5421020Smax.romanov@nginx.com     set_element(raw_headers, idx * 2 + 1, vstr);
543802Salexander.borisov@nginx.com }
544802Salexander.borisov@nginx.com 
545802Salexander.borisov@nginx.com 
546802Salexander.borisov@nginx.com napi_value
547802Salexander.borisov@nginx.com Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req)
548802Salexander.borisov@nginx.com {
5491022Smax.romanov@nginx.com     napi_value          constructor, res;
5501132Smax.romanov@nginx.com     req_data_t          *req_data;
5511022Smax.romanov@nginx.com     nxt_unit_request_t  *r;
5521022Smax.romanov@nginx.com 
5531022Smax.romanov@nginx.com     r = req->request;
554802Salexander.borisov@nginx.com 
5551132Smax.romanov@nginx.com     constructor = get_named_property(server_obj, "Socket");
556802Salexander.borisov@nginx.com 
5571022Smax.romanov@nginx.com     res = new_instance(constructor);
558802Salexander.borisov@nginx.com 
5591132Smax.romanov@nginx.com     req_data = (req_data_t *) req->data;
5601132Smax.romanov@nginx.com     req_data->sock_ref = wrap(res, req, sock_destroy);
5611132Smax.romanov@nginx.com 
5621022Smax.romanov@nginx.com     set_named_property(res, "remoteAddress", r->remote, r->remote_length);
5631022Smax.romanov@nginx.com     set_named_property(res, "localAddress", r->local, r->local_length);
564828Salexander.borisov@nginx.com 
5651022Smax.romanov@nginx.com     return res;
566802Salexander.borisov@nginx.com }
567802Salexander.borisov@nginx.com 
568802Salexander.borisov@nginx.com 
569802Salexander.borisov@nginx.com napi_value
570802Salexander.borisov@nginx.com Unit::create_request(napi_value server_obj, napi_value socket)
571802Salexander.borisov@nginx.com {
5721132Smax.romanov@nginx.com     napi_value  constructor;
5731132Smax.romanov@nginx.com 
5741132Smax.romanov@nginx.com     constructor = get_named_property(server_obj, "ServerRequest");
575802Salexander.borisov@nginx.com 
5761132Smax.romanov@nginx.com     return new_instance(constructor, server_obj, socket);
5771132Smax.romanov@nginx.com }
5781132Smax.romanov@nginx.com 
579802Salexander.borisov@nginx.com 
5801132Smax.romanov@nginx.com napi_value
5811132Smax.romanov@nginx.com Unit::create_response(napi_value server_obj, napi_value request,
5821132Smax.romanov@nginx.com     nxt_unit_request_info_t *req)
5831132Smax.romanov@nginx.com {
5841132Smax.romanov@nginx.com     napi_value  constructor, res;
5851132Smax.romanov@nginx.com     req_data_t  *req_data;
586802Salexander.borisov@nginx.com 
5871132Smax.romanov@nginx.com     constructor = get_named_property(server_obj, "ServerResponse");
5881132Smax.romanov@nginx.com 
5891132Smax.romanov@nginx.com     res = new_instance(constructor, request);
590802Salexander.borisov@nginx.com 
5911132Smax.romanov@nginx.com     req_data = (req_data_t *) req->data;
5921132Smax.romanov@nginx.com     req_data->resp_ref = wrap(res, req, resp_destroy);
5931132Smax.romanov@nginx.com 
5941132Smax.romanov@nginx.com     return res;
595802Salexander.borisov@nginx.com }
596802Salexander.borisov@nginx.com 
597802Salexander.borisov@nginx.com 
598802Salexander.borisov@nginx.com napi_value
5991132Smax.romanov@nginx.com Unit::create_websocket_frame(napi_value server_obj,
6001132Smax.romanov@nginx.com                              nxt_unit_websocket_frame_t *ws)
601802Salexander.borisov@nginx.com {
6021132Smax.romanov@nginx.com     void        *data;
6031132Smax.romanov@nginx.com     napi_value  constructor, res, buffer;
6041132Smax.romanov@nginx.com     uint8_t     sc[2];
6051132Smax.romanov@nginx.com 
6061132Smax.romanov@nginx.com     constructor = get_named_property(server_obj, "WebSocketFrame");
607802Salexander.borisov@nginx.com 
6081132Smax.romanov@nginx.com     res = new_instance(constructor);
6091132Smax.romanov@nginx.com 
6101132Smax.romanov@nginx.com     set_named_property(res, "fin", (bool) ws->header->fin);
6111132Smax.romanov@nginx.com     set_named_property(res, "opcode", ws->header->opcode);
6121132Smax.romanov@nginx.com     set_named_property(res, "length", (int64_t) ws->payload_len);
613802Salexander.borisov@nginx.com 
6141132Smax.romanov@nginx.com     if (ws->header->opcode == NXT_WEBSOCKET_OP_CLOSE) {
6151132Smax.romanov@nginx.com         if (ws->payload_len >= 2) {
6161132Smax.romanov@nginx.com             nxt_unit_websocket_read(ws, sc, 2);
6171132Smax.romanov@nginx.com 
6181132Smax.romanov@nginx.com             set_named_property(res, "closeStatus",
6191132Smax.romanov@nginx.com                                (((uint16_t) sc[0]) << 8) | sc[1]);
620802Salexander.borisov@nginx.com 
6211132Smax.romanov@nginx.com         } else {
6221132Smax.romanov@nginx.com             set_named_property(res, "closeStatus", -1);
6231132Smax.romanov@nginx.com         }
6241132Smax.romanov@nginx.com     }
625802Salexander.borisov@nginx.com 
6261132Smax.romanov@nginx.com     buffer = create_buffer((size_t) ws->content_length, &data);
6271132Smax.romanov@nginx.com     nxt_unit_websocket_read(ws, data, ws->content_length);
6281132Smax.romanov@nginx.com 
6291132Smax.romanov@nginx.com     set_named_property(res, "binaryPayload", buffer);
6301132Smax.romanov@nginx.com 
6311132Smax.romanov@nginx.com     return res;
632802Salexander.borisov@nginx.com }
633802Salexander.borisov@nginx.com 
634802Salexander.borisov@nginx.com 
635802Salexander.borisov@nginx.com napi_value
636802Salexander.borisov@nginx.com Unit::response_send_headers(napi_env env, napi_callback_info info)
637802Salexander.borisov@nginx.com {
638802Salexander.borisov@nginx.com     int                      ret;
639802Salexander.borisov@nginx.com     char                     *ptr, *name_ptr;
640802Salexander.borisov@nginx.com     bool                     is_array;
641802Salexander.borisov@nginx.com     size_t                   argc, name_len, value_len;
642802Salexander.borisov@nginx.com     uint32_t                 status_code, header_len, keys_len, array_len;
643802Salexander.borisov@nginx.com     uint32_t                 keys_count, i, j;
644802Salexander.borisov@nginx.com     uint16_t                 hash;
6451020Smax.romanov@nginx.com     nxt_napi                 napi(env);
646802Salexander.borisov@nginx.com     napi_value               this_arg, headers, keys, name, value, array_val;
6471132Smax.romanov@nginx.com     napi_value               array_entry;
648843Salexander.borisov@nginx.com     napi_valuetype           val_type;
649802Salexander.borisov@nginx.com     nxt_unit_field_t         *f;
650802Salexander.borisov@nginx.com     nxt_unit_request_info_t  *req;
6511132Smax.romanov@nginx.com     napi_value               argv[4];
652802Salexander.borisov@nginx.com 
6531132Smax.romanov@nginx.com     argc = 4;
654802Salexander.borisov@nginx.com 
6551020Smax.romanov@nginx.com     try {
6561020Smax.romanov@nginx.com         this_arg = napi.get_cb_info(info, argc, argv);
6571132Smax.romanov@nginx.com         if (argc != 4) {
6581020Smax.romanov@nginx.com             napi.throw_error("Wrong args count. Expected: "
6591020Smax.romanov@nginx.com                              "statusCode, headers, headers count, "
6601020Smax.romanov@nginx.com                              "headers length");
6611020Smax.romanov@nginx.com             return nullptr;
6621020Smax.romanov@nginx.com         }
663802Salexander.borisov@nginx.com 
6641132Smax.romanov@nginx.com         req = napi.get_request_info(this_arg);
6651132Smax.romanov@nginx.com         status_code = napi.get_value_uint32(argv[0]);
6661132Smax.romanov@nginx.com         keys_count = napi.get_value_uint32(argv[2]);
6671132Smax.romanov@nginx.com         header_len = napi.get_value_uint32(argv[3]);
668802Salexander.borisov@nginx.com 
6691132Smax.romanov@nginx.com         headers = argv[1];
670802Salexander.borisov@nginx.com 
6711020Smax.romanov@nginx.com         ret = nxt_unit_response_init(req, status_code, keys_count, header_len);
6721020Smax.romanov@nginx.com         if (ret != NXT_UNIT_OK) {
6731020Smax.romanov@nginx.com             napi.throw_error("Failed to create response");
6741020Smax.romanov@nginx.com             return nullptr;
675802Salexander.borisov@nginx.com         }
676802Salexander.borisov@nginx.com 
6771257Smax.romanov@nginx.com         /*
6781257Smax.romanov@nginx.com          * Each name and value are 0-terminated by libunit.
6791257Smax.romanov@nginx.com          * Need to add extra 2 bytes for each header.
6801257Smax.romanov@nginx.com          */
6811257Smax.romanov@nginx.com         header_len += keys_count * 2;
6821257Smax.romanov@nginx.com 
6831020Smax.romanov@nginx.com         keys = napi.get_property_names(headers);
6841020Smax.romanov@nginx.com         keys_len = napi.get_array_length(keys);
6851020Smax.romanov@nginx.com 
6861020Smax.romanov@nginx.com         ptr = req->response_buf->free;
687875Salexander.borisov@nginx.com 
6881020Smax.romanov@nginx.com         for (i = 0; i < keys_len; i++) {
6891020Smax.romanov@nginx.com             name = napi.get_element(keys, i);
6901020Smax.romanov@nginx.com 
6911020Smax.romanov@nginx.com             array_entry = napi.get_property(headers, name);
6921020Smax.romanov@nginx.com 
6931020Smax.romanov@nginx.com             name = napi.get_element(array_entry, 0);
6941020Smax.romanov@nginx.com             value = napi.get_element(array_entry, 1);
695875Salexander.borisov@nginx.com 
6961020Smax.romanov@nginx.com             name_len = napi.get_value_string_latin1(name, ptr, header_len);
6971020Smax.romanov@nginx.com             name_ptr = ptr;
6981020Smax.romanov@nginx.com 
6991257Smax.romanov@nginx.com             ptr += name_len + 1;
7001257Smax.romanov@nginx.com             header_len -= name_len + 1;
701802Salexander.borisov@nginx.com 
7021020Smax.romanov@nginx.com             hash = nxt_unit_field_hash(name_ptr, name_len);
7031020Smax.romanov@nginx.com 
7041020Smax.romanov@nginx.com             is_array = napi.is_array(value);
7051020Smax.romanov@nginx.com 
7061020Smax.romanov@nginx.com             if (is_array) {
7071020Smax.romanov@nginx.com                 array_len = napi.get_array_length(value);
708802Salexander.borisov@nginx.com 
7091020Smax.romanov@nginx.com                 for (j = 0; j < array_len; j++) {
7101020Smax.romanov@nginx.com                     array_val = napi.get_element(value, j);
7111020Smax.romanov@nginx.com 
7121020Smax.romanov@nginx.com                     val_type = napi.type_of(array_val);
713802Salexander.borisov@nginx.com 
7141020Smax.romanov@nginx.com                     if (val_type != napi_string) {
7151020Smax.romanov@nginx.com                         array_val = napi.coerce_to_string(array_val);
7161020Smax.romanov@nginx.com                     }
717802Salexander.borisov@nginx.com 
7181020Smax.romanov@nginx.com                     value_len = napi.get_value_string_latin1(array_val, ptr,
7191020Smax.romanov@nginx.com                                                              header_len);
720802Salexander.borisov@nginx.com 
7211020Smax.romanov@nginx.com                     f = req->response->fields + req->response->fields_count;
7221020Smax.romanov@nginx.com                     f->skip = 0;
7231020Smax.romanov@nginx.com 
7241020Smax.romanov@nginx.com                     nxt_unit_sptr_set(&f->name, name_ptr);
7251020Smax.romanov@nginx.com 
7261020Smax.romanov@nginx.com                     f->name_length = name_len;
7271020Smax.romanov@nginx.com                     f->hash = hash;
728802Salexander.borisov@nginx.com 
7291020Smax.romanov@nginx.com                     nxt_unit_sptr_set(&f->value, ptr);
7301020Smax.romanov@nginx.com                     f->value_length = (uint32_t) value_len;
731802Salexander.borisov@nginx.com 
7321257Smax.romanov@nginx.com                     ptr += value_len + 1;
7331257Smax.romanov@nginx.com                     header_len -= value_len + 1;
7341020Smax.romanov@nginx.com 
7351020Smax.romanov@nginx.com                     req->response->fields_count++;
736802Salexander.borisov@nginx.com                 }
737802Salexander.borisov@nginx.com 
7381020Smax.romanov@nginx.com             } else {
7391020Smax.romanov@nginx.com                 val_type = napi.type_of(value);
740843Salexander.borisov@nginx.com 
741843Salexander.borisov@nginx.com                 if (val_type != napi_string) {
7421020Smax.romanov@nginx.com                     value = napi.coerce_to_string(value);
743843Salexander.borisov@nginx.com                 }
744843Salexander.borisov@nginx.com 
7451020Smax.romanov@nginx.com                 value_len = napi.get_value_string_latin1(value, ptr, header_len);
746802Salexander.borisov@nginx.com 
747802Salexander.borisov@nginx.com                 f = req->response->fields + req->response->fields_count;
748802Salexander.borisov@nginx.com                 f->skip = 0;
749802Salexander.borisov@nginx.com 
750802Salexander.borisov@nginx.com                 nxt_unit_sptr_set(&f->name, name_ptr);
751802Salexander.borisov@nginx.com 
752802Salexander.borisov@nginx.com                 f->name_length = name_len;
753802Salexander.borisov@nginx.com                 f->hash = hash;
754802Salexander.borisov@nginx.com 
755802Salexander.borisov@nginx.com                 nxt_unit_sptr_set(&f->value, ptr);
756802Salexander.borisov@nginx.com                 f->value_length = (uint32_t) value_len;
757802Salexander.borisov@nginx.com 
7581257Smax.romanov@nginx.com                 ptr += value_len + 1;
7591257Smax.romanov@nginx.com                 header_len -= value_len + 1;
760802Salexander.borisov@nginx.com 
761802Salexander.borisov@nginx.com                 req->response->fields_count++;
762802Salexander.borisov@nginx.com             }
7631020Smax.romanov@nginx.com         }
764843Salexander.borisov@nginx.com 
7651020Smax.romanov@nginx.com     } catch (exception &e) {
7661020Smax.romanov@nginx.com         napi.throw_error(e);
7671020Smax.romanov@nginx.com         return nullptr;
768802Salexander.borisov@nginx.com     }
769802Salexander.borisov@nginx.com 
770802Salexander.borisov@nginx.com     req->response_buf->free = ptr;
771802Salexander.borisov@nginx.com 
772802Salexander.borisov@nginx.com     ret = nxt_unit_response_send(req);
773802Salexander.borisov@nginx.com     if (ret != NXT_UNIT_OK) {
7741020Smax.romanov@nginx.com         napi.throw_error("Failed to send response");
7751020Smax.romanov@nginx.com         return nullptr;
776802Salexander.borisov@nginx.com     }
777802Salexander.borisov@nginx.com 
778802Salexander.borisov@nginx.com     return this_arg;
779802Salexander.borisov@nginx.com }
780802Salexander.borisov@nginx.com 
781802Salexander.borisov@nginx.com 
782802Salexander.borisov@nginx.com napi_value
783802Salexander.borisov@nginx.com Unit::response_write(napi_env env, napi_callback_info info)
784802Salexander.borisov@nginx.com {
785802Salexander.borisov@nginx.com     int                      ret;
7861132Smax.romanov@nginx.com     void                     *ptr;
787802Salexander.borisov@nginx.com     size_t                   argc, have_buf_len;
788*1322Smax.romanov@nginx.com     ssize_t                  res_len;
789*1322Smax.romanov@nginx.com     uint32_t                 buf_start, buf_len;
7901020Smax.romanov@nginx.com     nxt_napi                 napi(env);
7911132Smax.romanov@nginx.com     napi_value               this_arg;
792802Salexander.borisov@nginx.com     nxt_unit_buf_t           *buf;
793802Salexander.borisov@nginx.com     napi_valuetype           buf_type;
794802Salexander.borisov@nginx.com     nxt_unit_request_info_t  *req;
795*1322Smax.romanov@nginx.com     napi_value               argv[3];
796802Salexander.borisov@nginx.com 
797*1322Smax.romanov@nginx.com     argc = 3;
798802Salexander.borisov@nginx.com 
7991020Smax.romanov@nginx.com     try {
8001020Smax.romanov@nginx.com         this_arg = napi.get_cb_info(info, argc, argv);
801*1322Smax.romanov@nginx.com         if (argc != 3) {
8021020Smax.romanov@nginx.com             throw exception("Wrong args count. Expected: "
803*1322Smax.romanov@nginx.com                             "chunk, start, length");
8041020Smax.romanov@nginx.com         }
805802Salexander.borisov@nginx.com 
8061132Smax.romanov@nginx.com         req = napi.get_request_info(this_arg);
8071132Smax.romanov@nginx.com         buf_type = napi.type_of(argv[0]);
808*1322Smax.romanov@nginx.com         buf_start = napi.get_value_uint32(argv[1]);
809*1322Smax.romanov@nginx.com         buf_len = napi.get_value_uint32(argv[2]) + 1;
8101132Smax.romanov@nginx.com 
8111132Smax.romanov@nginx.com         if (buf_type == napi_string) {
8121132Smax.romanov@nginx.com             /* TODO: will work only for utf8 content-type */
813802Salexander.borisov@nginx.com 
814*1322Smax.romanov@nginx.com             if (req->response_buf != NULL
815*1322Smax.romanov@nginx.com                 && (req->response_buf->end - req->response_buf->free)
816*1322Smax.romanov@nginx.com                     >= buf_len)
817*1322Smax.romanov@nginx.com             {
818*1322Smax.romanov@nginx.com                 buf = req->response_buf;
819*1322Smax.romanov@nginx.com 
820*1322Smax.romanov@nginx.com             } else {
821*1322Smax.romanov@nginx.com                 buf = nxt_unit_response_buf_alloc(req, buf_len);
822*1322Smax.romanov@nginx.com                 if (buf == NULL) {
823*1322Smax.romanov@nginx.com                     throw exception("Failed to allocate response buffer");
824*1322Smax.romanov@nginx.com                 }
825*1322Smax.romanov@nginx.com             }
826*1322Smax.romanov@nginx.com 
8271132Smax.romanov@nginx.com             have_buf_len = napi.get_value_string_utf8(argv[0], buf->free,
8281132Smax.romanov@nginx.com                                                       buf_len);
8291132Smax.romanov@nginx.com 
830*1322Smax.romanov@nginx.com             buf->free += have_buf_len;
831*1322Smax.romanov@nginx.com 
832*1322Smax.romanov@nginx.com             ret = nxt_unit_buf_send(buf);
833*1322Smax.romanov@nginx.com             if (ret == NXT_UNIT_OK) {
834*1322Smax.romanov@nginx.com                 res_len = have_buf_len;
835*1322Smax.romanov@nginx.com             }
836*1322Smax.romanov@nginx.com 
8371132Smax.romanov@nginx.com         } else {
8381132Smax.romanov@nginx.com             ptr = napi.get_buffer_info(argv[0], have_buf_len);
839802Salexander.borisov@nginx.com 
840*1322Smax.romanov@nginx.com             if (buf_start > 0) {
841*1322Smax.romanov@nginx.com                 ptr = ((uint8_t *) ptr) + buf_start;
842*1322Smax.romanov@nginx.com                 have_buf_len -= buf_start;
843*1322Smax.romanov@nginx.com             }
844*1322Smax.romanov@nginx.com 
845*1322Smax.romanov@nginx.com             res_len = nxt_unit_response_write_nb(req, ptr, have_buf_len, 0);
846*1322Smax.romanov@nginx.com 
847*1322Smax.romanov@nginx.com             ret = res_len < 0 ? -res_len : NXT_UNIT_OK;
8481132Smax.romanov@nginx.com         }
8491132Smax.romanov@nginx.com 
8501132Smax.romanov@nginx.com         if (ret != NXT_UNIT_OK) {
8511132Smax.romanov@nginx.com             throw exception("Failed to send body buf");
8521132Smax.romanov@nginx.com         }
8531020Smax.romanov@nginx.com     } catch (exception &e) {
8541020Smax.romanov@nginx.com         napi.throw_error(e);
8551020Smax.romanov@nginx.com         return nullptr;
856802Salexander.borisov@nginx.com     }
857802Salexander.borisov@nginx.com 
858*1322Smax.romanov@nginx.com     return napi.create((int64_t) res_len);
859802Salexander.borisov@nginx.com }
860802Salexander.borisov@nginx.com 
861802Salexander.borisov@nginx.com 
862802Salexander.borisov@nginx.com napi_value
863802Salexander.borisov@nginx.com Unit::response_end(napi_env env, napi_callback_info info)
864802Salexander.borisov@nginx.com {
8651020Smax.romanov@nginx.com     nxt_napi                 napi(env);
8661132Smax.romanov@nginx.com     napi_value               this_arg;
8671132Smax.romanov@nginx.com     req_data_t               *req_data;
868802Salexander.borisov@nginx.com     nxt_unit_request_info_t  *req;
869802Salexander.borisov@nginx.com 
8701132Smax.romanov@nginx.com     try {
8711132Smax.romanov@nginx.com         this_arg = napi.get_cb_info(info);
8721132Smax.romanov@nginx.com 
8731132Smax.romanov@nginx.com         req = napi.get_request_info(this_arg);
874802Salexander.borisov@nginx.com 
8751132Smax.romanov@nginx.com         req_data = (req_data_t *) req->data;
8761020Smax.romanov@nginx.com 
8771132Smax.romanov@nginx.com         napi.remove_wrap(req_data->sock_ref);
8781132Smax.romanov@nginx.com         napi.remove_wrap(req_data->resp_ref);
8791132Smax.romanov@nginx.com         napi.remove_wrap(req_data->conn_ref);
8801020Smax.romanov@nginx.com 
8811020Smax.romanov@nginx.com     } catch (exception &e) {
8821020Smax.romanov@nginx.com         napi.throw_error(e);
883802Salexander.borisov@nginx.com         return nullptr;
884802Salexander.borisov@nginx.com     }
885802Salexander.borisov@nginx.com 
886802Salexander.borisov@nginx.com     nxt_unit_request_done(req, NXT_UNIT_OK);
887802Salexander.borisov@nginx.com 
888802Salexander.borisov@nginx.com     return this_arg;
889802Salexander.borisov@nginx.com }
8901132Smax.romanov@nginx.com 
8911132Smax.romanov@nginx.com 
8921132Smax.romanov@nginx.com napi_value
8931132Smax.romanov@nginx.com Unit::websocket_send_frame(napi_env env, napi_callback_info info)
8941132Smax.romanov@nginx.com {
8951132Smax.romanov@nginx.com     int                      ret, iovec_len;
8961132Smax.romanov@nginx.com     bool                     fin;
8971132Smax.romanov@nginx.com     size_t                   buf_len;
8981132Smax.romanov@nginx.com     uint32_t                 opcode, sc;
8991132Smax.romanov@nginx.com     nxt_napi                 napi(env);
9001132Smax.romanov@nginx.com     napi_value               this_arg, frame, payload;
9011132Smax.romanov@nginx.com     nxt_unit_request_info_t  *req;
9021132Smax.romanov@nginx.com     char                     status_code[2];
9031132Smax.romanov@nginx.com     struct iovec             iov[2];
9041132Smax.romanov@nginx.com 
9051132Smax.romanov@nginx.com     iovec_len = 0;
9061132Smax.romanov@nginx.com 
9071132Smax.romanov@nginx.com     try {
9081132Smax.romanov@nginx.com         this_arg = napi.get_cb_info(info, frame);
9091132Smax.romanov@nginx.com 
9101132Smax.romanov@nginx.com         req = napi.get_request_info(this_arg);
9111132Smax.romanov@nginx.com 
9121132Smax.romanov@nginx.com         opcode = napi.get_value_uint32(napi.get_named_property(frame,
9131132Smax.romanov@nginx.com                                                                "opcode"));
9141132Smax.romanov@nginx.com         if (opcode == NXT_WEBSOCKET_OP_CLOSE) {
9151132Smax.romanov@nginx.com             sc = napi.get_value_uint32(napi.get_named_property(frame,
9161132Smax.romanov@nginx.com                                                                "closeStatus"));
9171132Smax.romanov@nginx.com             status_code[0] = (sc >> 8) & 0xFF;
9181132Smax.romanov@nginx.com             status_code[1] = sc & 0xFF;
9191132Smax.romanov@nginx.com 
9201132Smax.romanov@nginx.com             iov[iovec_len].iov_base = status_code;
9211132Smax.romanov@nginx.com             iov[iovec_len].iov_len = 2;
9221132Smax.romanov@nginx.com             iovec_len++;
9231132Smax.romanov@nginx.com         }
9241132Smax.romanov@nginx.com 
9251132Smax.romanov@nginx.com         try {
9261132Smax.romanov@nginx.com             fin = napi.get_value_bool(napi.get_named_property(frame, "fin"));
9271132Smax.romanov@nginx.com 
9281132Smax.romanov@nginx.com         } catch (exception &e) {
9291132Smax.romanov@nginx.com             fin = true;
9301132Smax.romanov@nginx.com         }
9311132Smax.romanov@nginx.com 
9321132Smax.romanov@nginx.com         payload = napi.get_named_property(frame, "binaryPayload");
9331132Smax.romanov@nginx.com 
9341132Smax.romanov@nginx.com         if (napi.is_buffer(payload)) {
9351132Smax.romanov@nginx.com             iov[iovec_len].iov_base = napi.get_buffer_info(payload, buf_len);
9361132Smax.romanov@nginx.com 
9371132Smax.romanov@nginx.com         } else {
9381132Smax.romanov@nginx.com             buf_len = 0;
9391132Smax.romanov@nginx.com         }
9401132Smax.romanov@nginx.com 
9411132Smax.romanov@nginx.com     } catch (exception &e) {
9421132Smax.romanov@nginx.com         napi.throw_error(e);
9431132Smax.romanov@nginx.com         return nullptr;
9441132Smax.romanov@nginx.com     }
9451132Smax.romanov@nginx.com 
9461132Smax.romanov@nginx.com     if (buf_len > 0) {
9471132Smax.romanov@nginx.com         iov[iovec_len].iov_len = buf_len;
9481132Smax.romanov@nginx.com         iovec_len++;
9491132Smax.romanov@nginx.com     }
9501132Smax.romanov@nginx.com 
9511132Smax.romanov@nginx.com     ret = nxt_unit_websocket_sendv(req, opcode, fin ? 1 : 0, iov, iovec_len);
9521132Smax.romanov@nginx.com     if (ret != NXT_UNIT_OK) {
9531132Smax.romanov@nginx.com         goto failed;
9541132Smax.romanov@nginx.com     }
9551132Smax.romanov@nginx.com 
9561132Smax.romanov@nginx.com     return this_arg;
9571132Smax.romanov@nginx.com 
9581132Smax.romanov@nginx.com failed:
9591132Smax.romanov@nginx.com 
9601132Smax.romanov@nginx.com     napi.throw_error("Failed to send frame");
9611132Smax.romanov@nginx.com 
9621132Smax.romanov@nginx.com     return nullptr;
9631132Smax.romanov@nginx.com }
9641132Smax.romanov@nginx.com 
9651132Smax.romanov@nginx.com 
9661132Smax.romanov@nginx.com napi_value
9671132Smax.romanov@nginx.com Unit::websocket_set_sock(napi_env env, napi_callback_info info)
9681132Smax.romanov@nginx.com {
9691132Smax.romanov@nginx.com     nxt_napi                 napi(env);
9701132Smax.romanov@nginx.com     napi_value               this_arg, sock;
9711132Smax.romanov@nginx.com     req_data_t               *req_data;
9721132Smax.romanov@nginx.com     nxt_unit_request_info_t  *req;
9731132Smax.romanov@nginx.com 
9741132Smax.romanov@nginx.com     try {
9751132Smax.romanov@nginx.com         this_arg = napi.get_cb_info(info, sock);
9761132Smax.romanov@nginx.com 
9771132Smax.romanov@nginx.com         req = napi.get_request_info(sock);
9781132Smax.romanov@nginx.com 
9791132Smax.romanov@nginx.com         req_data = (req_data_t *) req->data;
9801132Smax.romanov@nginx.com         req_data->conn_ref = napi.wrap(this_arg, req, conn_destroy);
9811132Smax.romanov@nginx.com 
9821132Smax.romanov@nginx.com     } catch (exception &e) {
9831132Smax.romanov@nginx.com         napi.throw_error(e);
9841132Smax.romanov@nginx.com         return nullptr;
9851132Smax.romanov@nginx.com     }
9861132Smax.romanov@nginx.com 
9871132Smax.romanov@nginx.com     return this_arg;
9881132Smax.romanov@nginx.com }
9891132Smax.romanov@nginx.com 
9901132Smax.romanov@nginx.com 
9911132Smax.romanov@nginx.com void
9921132Smax.romanov@nginx.com Unit::conn_destroy(napi_env env, void *nativeObject, void *finalize_hint)
9931132Smax.romanov@nginx.com {
9941132Smax.romanov@nginx.com     nxt_unit_request_info_t  *req;
9951132Smax.romanov@nginx.com 
9961132Smax.romanov@nginx.com     req = (nxt_unit_request_info_t *) nativeObject;
9971132Smax.romanov@nginx.com 
9981132Smax.romanov@nginx.com     nxt_unit_warn(NULL, "conn_destroy: %p", req);
9991132Smax.romanov@nginx.com }
10001132Smax.romanov@nginx.com 
10011132Smax.romanov@nginx.com 
10021132Smax.romanov@nginx.com void
10031132Smax.romanov@nginx.com Unit::sock_destroy(napi_env env, void *nativeObject, void *finalize_hint)
10041132Smax.romanov@nginx.com {
10051132Smax.romanov@nginx.com     nxt_unit_request_info_t  *req;
10061132Smax.romanov@nginx.com 
10071132Smax.romanov@nginx.com     req = (nxt_unit_request_info_t *) nativeObject;
10081132Smax.romanov@nginx.com 
10091132Smax.romanov@nginx.com     nxt_unit_warn(NULL, "sock_destroy: %p", req);
10101132Smax.romanov@nginx.com }
10111132Smax.romanov@nginx.com 
10121132Smax.romanov@nginx.com 
10131132Smax.romanov@nginx.com void
10141132Smax.romanov@nginx.com Unit::resp_destroy(napi_env env, void *nativeObject, void *finalize_hint)
10151132Smax.romanov@nginx.com {
10161132Smax.romanov@nginx.com     nxt_unit_request_info_t  *req;
10171132Smax.romanov@nginx.com 
10181132Smax.romanov@nginx.com     req = (nxt_unit_request_info_t *) nativeObject;
10191132Smax.romanov@nginx.com 
10201132Smax.romanov@nginx.com     nxt_unit_warn(NULL, "resp_destroy: %p", req);
10211132Smax.romanov@nginx.com }
1022