From 425d56037247480eb9c3f839fb79a88f2aa2f36b Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Fri, 28 Sep 2018 15:05:35 +0300 Subject: Version bump. --- src/nxt_main.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nxt_main.h b/src/nxt_main.h index 1bbd9ee5..670010f8 100644 --- a/src/nxt_main.h +++ b/src/nxt_main.h @@ -11,8 +11,8 @@ #include -#define NXT_VERSION "1.4" -#define NXT_VERNUM 10400 +#define NXT_VERSION "1.5" +#define NXT_VERNUM 10500 #define NXT_SERVER "Unit/" NXT_VERSION -- cgit From 92da8dae1e4194698ace12e6a51df5d9431e00c4 Mon Sep 17 00:00:00 2001 From: Igor Sysoev Date: Fri, 28 Sep 2018 15:12:47 +0300 Subject: Fixed bug in socket write error handling. The bug has been introduced in e3972a4a9c73. --- src/nxt_conn_write.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_conn_write.c b/src/nxt_conn_write.c index 73c3fa02..80d6f5cf 100644 --- a/src/nxt_conn_write.c +++ b/src/nxt_conn_write.c @@ -127,7 +127,9 @@ nxt_conn_io_write(nxt_task_t *task, void *obj, void *data) return; } - /* ret == NXT_ERROR */ + if (ret != NXT_ERROR) { + return; + } nxt_fd_event_block_write(engine, &c->socket); -- cgit From b5d4fc939edca5ea5858b66d5fcf789b660df243 Mon Sep 17 00:00:00 2001 From: Igor Sysoev Date: Fri, 28 Sep 2018 17:32:02 +0300 Subject: Disabled chunked transfer encoding for 204 responses. --- src/nxt_h1proto.c | 3 ++- src/nxt_http.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c index 3e5044b7..70d6e5d8 100644 --- a/src/nxt_h1proto.c +++ b/src/nxt_h1proto.c @@ -967,7 +967,8 @@ nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r) http11 = (h1p->parser.version.s.minor != '0'); if (r->resp.content_length == NULL || r->resp.content_length->skip) { - if (http11) { + + if (http11 && n != NXT_HTTP_NO_CONTENT) { h1p->chunked = 1; size += nxt_length(chunked); /* Trailing CRLF will be added by the first chunk header. */ diff --git a/src/nxt_http.h b/src/nxt_http.h index 1c7ed8bb..b2111f90 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -12,6 +12,7 @@ typedef enum { NXT_HTTP_INVALID = 0, NXT_HTTP_OK = 200, + NXT_HTTP_NO_CONTENT = 204, NXT_HTTP_MULTIPLE_CHOICES = 300, NXT_HTTP_MOVED_PERMANENTLY = 301, -- cgit From e51ab1b2cbb10ef5a48d67ce7f492ddca481a5b7 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Mon, 1 Oct 2018 15:06:31 +0300 Subject: Fixed error log time caching resolution. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to 洪志道 (Hong Zhi Dao). --- src/nxt_app_log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_app_log.c b/src/nxt_app_log.c index 83eac371..0ecd9aac 100644 --- a/src/nxt_app_log.c +++ b/src/nxt_app_log.c @@ -97,7 +97,7 @@ static nxt_time_string_t nxt_log_error_time_cache = { "%4d/%02d/%02d %02d:%02d:%02d ", nxt_length("1970/09/28 12:00:00 "), NXT_THREAD_TIME_LOCAL, - NXT_THREAD_TIME_MSEC, + NXT_THREAD_TIME_SEC, }; -- cgit From 3c2eddb183684e577bca967543a52067edbda9a5 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Mon, 1 Oct 2018 15:06:31 +0300 Subject: Allowing keep-alive connections after 204 responses. This was unintentionally disabled by 7b5026a0bdeb. --- src/nxt_h1proto.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c index 70d6e5d8..ed159c93 100644 --- a/src/nxt_h1proto.c +++ b/src/nxt_h1proto.c @@ -968,11 +968,13 @@ nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r) if (r->resp.content_length == NULL || r->resp.content_length->skip) { - if (http11 && n != NXT_HTTP_NO_CONTENT) { - h1p->chunked = 1; - size += nxt_length(chunked); - /* Trailing CRLF will be added by the first chunk header. */ - size -= nxt_length("\r\n"); + if (http11) { + if (n != NXT_HTTP_NO_CONTENT) { + h1p->chunked = 1; + size += nxt_length(chunked); + /* Trailing CRLF will be added by the first chunk header. */ + size -= nxt_length("\r\n"); + } } else { h1p->keepalive = 0; -- cgit From d54d806c52756ab65d61713589029627ffa72c5d Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Mon, 1 Oct 2018 15:06:31 +0300 Subject: Disabled chunked transfer encoding for 304 responses as well. According to RFC 7232: | A 304 response cannot contain a message-body; it is always terminated | by the first empty line after the header fields. --- src/nxt_h1proto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c index ed159c93..2194e56f 100644 --- a/src/nxt_h1proto.c +++ b/src/nxt_h1proto.c @@ -969,7 +969,7 @@ nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r) if (r->resp.content_length == NULL || r->resp.content_length->skip) { if (http11) { - if (n != NXT_HTTP_NO_CONTENT) { + if (n != NXT_HTTP_NOT_MODIFIED && n != NXT_HTTP_NO_CONTENT) { h1p->chunked = 1; size += nxt_length(chunked); /* Trailing CRLF will be added by the first chunk header. */ -- cgit From cb1b0744930801b6e06d05f788c3e7cb40163167 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 2 Oct 2018 19:46:06 +0300 Subject: Making port fd blocking on app side and non-blocking in Unit. This issue was introduced in libunit commit (e0f0cd7d244a). All port sockets in application should be in blocking mode whereas Unit itself operates non-blocking sockets. Having non-blocking sockets in application may cause send error during intensive response packets generation. See https://mailman.nginx.org/pipermail/unit/2018-October/000080.html. --- src/nxt_port.c | 2 ++ src/nxt_unit.c | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_port.c b/src/nxt_port.c index 3d18fa67..30719ad3 100644 --- a/src/nxt_port.c +++ b/src/nxt_port.c @@ -276,6 +276,8 @@ nxt_port_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_process_use(task, process, -1); + nxt_fd_nonblocking(task, msg->fd); + port->pair[0] = -1; port->pair[1] = msg->fd; port->max_size = new_port_msg->max_size; diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 0f46dc7e..251c5219 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -510,7 +510,7 @@ int nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, void *buf, size_t buf_size, void *oob, size_t oob_size) { - int fd, rc; + int fd, rc, nb; pid_t pid; nxt_queue_t incoming_buf; struct cmsghdr *cm; @@ -611,6 +611,15 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, port_msg->stream, (int) new_port_msg->pid, (int) new_port_msg->id, fd); + nb = 0; + + if (nxt_slow_path(ioctl(fd, FIONBIO, &nb) == -1)) { + nxt_unit_alert(ctx, "#%"PRIu32": new_port: ioctl(%d, FIONBIO, 0) " + "failed: %s (%d)", fd, strerror(errno), errno); + + goto fail; + } + nxt_unit_port_id_init(&new_port.id, new_port_msg->pid, new_port_msg->id); -- cgit From c8b30ef4da02278308e0ed050d9a937134a6aa45 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 2 Oct 2018 19:49:48 +0300 Subject: Go: fixing typo in log messages. --- src/go/unit/nxt_cgo_lib.c | 2 +- src/go/unit/port.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/go/unit/nxt_cgo_lib.c b/src/go/unit/nxt_cgo_lib.c index 9c080730..172bef88 100644 --- a/src/go/unit/nxt_cgo_lib.c +++ b/src/go/unit/nxt_cgo_lib.c @@ -203,5 +203,5 @@ nxt_cgo_request_done(uintptr_t req, int res) void nxt_cgo_warn(uintptr_t msg, uint32_t msg_len) { - nxt_unit_warn(NULL, ".*s", (int) msg_len, (char *) msg); + nxt_unit_warn(NULL, "%.*s", (int) msg_len, (char *) msg); } diff --git a/src/go/unit/port.go b/src/go/unit/port.go index f716c9ec..a68cae74 100644 --- a/src/go/unit/port.go +++ b/src/go/unit/port.go @@ -163,7 +163,7 @@ func nxt_go_port_recv(pid C.int, id C.int, buf unsafe.Pointer, buf_size C.int, GoBytes(oob, oob_size)) if err != nil { - nxt_go_warn("write result %d (%d), %s", n, oobn, err) + nxt_go_warn("read result %d (%d), %s", n, oobn, err) } return C.ssize_t(n) -- cgit From 141ee2aa326d54eeecc2ef96f61e2d2d8068b6b1 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 2 Oct 2018 19:49:49 +0300 Subject: Filling cmsghdr with 0 to pass Go 1.11 message validation. --- src/nxt_unit.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 251c5219..0d1be557 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -2167,9 +2167,12 @@ nxt_unit_send_mmap(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, int fd) msg.mf = 0; msg.tracking = 0; -#if (NXT_VALGRIND) + /* + * Fill all padding fields with 0. + * Code in Go 1.11 validate cmsghdr using padding field as part of len. + * See Cmsghdr definition and socketControlMessageHeaderAndData function. + */ memset(&cmsg, 0, sizeof(cmsg)); -#endif cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int)); cmsg.cm.cmsg_level = SOL_SOCKET; @@ -3001,9 +3004,7 @@ nxt_unit_send_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *dst, m.new_port.max_size = 16 * 1024; m.new_port.max_share = 64 * 1024; -#if (NXT_VALGRIND) memset(&cmsg, 0, sizeof(cmsg)); -#endif cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int)); cmsg.cm.cmsg_level = SOL_SOCKET; -- cgit From ea62327b008b39dc48a51aa80343b20a0a122cd6 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 3 Oct 2018 17:50:03 +0300 Subject: Added Node.js support. --- src/nodejs/unit-http/README.md | 2 + src/nodejs/unit-http/addon.cpp | 15 + src/nodejs/unit-http/binding.gyp | 12 + src/nodejs/unit-http/binding_pub.gyp | 7 + src/nodejs/unit-http/http.js | 23 + src/nodejs/unit-http/http_server.js | 331 +++++++++++++ src/nodejs/unit-http/package.json | 29 ++ src/nodejs/unit-http/socket.js | 99 ++++ src/nodejs/unit-http/unit.cpp | 905 +++++++++++++++++++++++++++++++++++ src/nodejs/unit-http/unit.h | 77 +++ 10 files changed, 1500 insertions(+) create mode 100644 src/nodejs/unit-http/README.md create mode 100644 src/nodejs/unit-http/addon.cpp create mode 100644 src/nodejs/unit-http/binding.gyp create mode 100644 src/nodejs/unit-http/binding_pub.gyp create mode 100755 src/nodejs/unit-http/http.js create mode 100755 src/nodejs/unit-http/http_server.js create mode 100644 src/nodejs/unit-http/package.json create mode 100755 src/nodejs/unit-http/socket.js create mode 100644 src/nodejs/unit-http/unit.cpp create mode 100644 src/nodejs/unit-http/unit.h (limited to 'src') diff --git a/src/nodejs/unit-http/README.md b/src/nodejs/unit-http/README.md new file mode 100644 index 00000000..139597f9 --- /dev/null +++ b/src/nodejs/unit-http/README.md @@ -0,0 +1,2 @@ + + diff --git a/src/nodejs/unit-http/addon.cpp b/src/nodejs/unit-http/addon.cpp new file mode 100644 index 00000000..6ced9538 --- /dev/null +++ b/src/nodejs/unit-http/addon.cpp @@ -0,0 +1,15 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#include "unit.h" + + +napi_value +Init(napi_env env, napi_value exports) +{ + return Unit::init(env, exports); +} + +NAPI_MODULE(Unit, Init) diff --git a/src/nodejs/unit-http/binding.gyp b/src/nodejs/unit-http/binding.gyp new file mode 100644 index 00000000..171c2eb7 --- /dev/null +++ b/src/nodejs/unit-http/binding.gyp @@ -0,0 +1,12 @@ +{ + 'targets': [{ + 'target_name': "unit-http", + 'sources': ["unit.cpp", "addon.cpp"], + 'include_dirs': [ + " 999) { + throw new ERR_HTTP_INVALID_STATUS_CODE(originalStatusCode); + } + + if (typeof reason === 'string') { + this.statusMessage = reason; + + } else { + if (!this.statusMessage) { + this.statusMessage = http.STATUS_CODES[statusCode] || 'unknown'; + } + + obj = reason; + } + + this.statusCode = statusCode; + + if (obj) { + var k; + var keys = Object.keys(obj); + + for (var i = 0; i < keys.length; i++) { + k = keys[i]; + + if (k) { + this.setHeader(k, obj[k]); + } + } + } + + unit_lib.unit_response_headers(this, statusCode, this.headers, this.headers_count, this.headers_len); + + this.headersSent = true; +}; + +ServerResponse.prototype._writeBody = function(chunk, encoding, callback) { + var contentLength = 0; + + if (!this.headersSent) { + this.writeHead(this.statusCode); + } + + if (this.finished) { + return this; + } + + if (typeof chunk === 'function') { + callback = chunk; + chunk = null; + + } else if (typeof encoding === 'function') { + callback = encoding; + encoding = null; + } + + if (chunk) { + if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) { + throw new TypeError('First argument must be a string or Buffer'); + } + + if (typeof chunk === 'string') { + contentLength = Buffer.byteLength(chunk, encoding); + + } else { + contentLength = chunk.length; + } + + unit_lib.unit_response_write(this, chunk, contentLength); + } + + if (typeof callback === 'function') { + callback(this); + } +}; + +ServerResponse.prototype.write = function write(chunk, encoding, callback) { + this._writeBody(chunk, encoding, callback); + + return this; +}; + +ServerResponse.prototype.end = function end(chunk, encoding, callback) { + this._writeBody(chunk, encoding, callback); + unit_lib.unit_response_end(this) + + this.finished = true; + + return this; +}; + +function ServerRequest(server) { + EventEmitter.call(this); + + this.server = server; +} +util.inherits(ServerRequest, EventEmitter); + +ServerRequest.prototype.unpipe = undefined; + +ServerRequest.prototype.setTimeout = function setTimeout(msecs, callback) { + this.timeout = msecs; + + if (callback) { + this.on('timeout', callback); + } + + return this; +}; + +ServerRequest.prototype.statusCode = function statusCode() { + /* Only valid for response obtained from http.ClientRequest. */ +}; + +ServerRequest.prototype.statusMessage = function statusMessage() { + /* Only valid for response obtained from http.ClientRequest. */ +}; + +ServerRequest.prototype.trailers = function trailers() { + throw new Error("Not supported"); +}; + +ServerRequest.prototype.METHODS = function METHODS() { + return http.METHODS; +}; + +ServerRequest.prototype.STATUS_CODES = function STATUS_CODES() { + return http.STATUS_CODES; +}; + +ServerRequest.prototype.listeners = function listeners() { + return []; +}; + +ServerRequest.prototype.resume = function resume() { + return []; +}; + +function Server(requestListener) { + EventEmitter.call(this); + + this.unit = new unit_lib.Unit(); + this.unit.createServer(); + + this.unit.server = this; + + this.socket = Socket; + this.request = ServerRequest; + this.response = ServerResponse; + + if (requestListener) { + this.on('request', requestListener); + } +} +util.inherits(Server, EventEmitter); + +Server.prototype.setTimeout = function setTimeout(msecs, callback) { + this.timeout = msecs; + + if (callback) { + this.on('timeout', callback); + } + + return this; +}; + +Server.prototype.listen = function () { + this.unit.listen(); +}; + +function connectionListener(socket) { +} + + +module.exports = { + STATUS_CODES: http.STATUS_CODES, + Server, + ServerResponse, + ServerRequest, + _connectionListener: connectionListener +}; diff --git a/src/nodejs/unit-http/package.json b/src/nodejs/unit-http/package.json new file mode 100644 index 00000000..bc481bc9 --- /dev/null +++ b/src/nodejs/unit-http/package.json @@ -0,0 +1,29 @@ +{ + "name": "unit-http", + "version": "1.0.0", + "description": "HTTP module for NGINX Unit", + "main": "http.js", + "files": [ + "addon.cpp", + "binding.gyp", + "http_server.js", + "http.js", + "package.json", + "socket.js", + "unit.cpp", + "unit.h", + "README.md" + ], + "scripts": { + "clean": "node-gyp clean", + "configure": "node-gyp configure", + "build": "node-gyp build", + "install": "node-gyp configure build" + }, + "author": "Alexander Borisov", + "license": "Apache 2.0", + "gypfile": true, + "dependencies": { + "node-addon-api": "1.2.0" + } +} diff --git a/src/nodejs/unit-http/socket.js b/src/nodejs/unit-http/socket.js new file mode 100755 index 00000000..89702834 --- /dev/null +++ b/src/nodejs/unit-http/socket.js @@ -0,0 +1,99 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +'use strict'; + +const EventEmitter = require('events'); +const util = require('util'); +const unit_lib = require('unit-http/build/Release/unit-http.node'); + +function Socket(options) { + EventEmitter.call(this); + + if (typeof options === 'number') { + options = { fd: options }; + + } else if (options === undefined) { + options = {}; + } + + this.readable = options.readable !== false; + this.writable = options.writable !== false; +} +util.inherits(Socket, EventEmitter); + +Socket.prototype.bufferSize = 0; +Socket.prototype.bytesRead = 0; +Socket.prototype.bytesWritten = 0; +Socket.prototype.connecting = false; +Socket.prototype.destroyed = false; +Socket.prototype.localAddress = ""; +Socket.prototype.localPort = 0; +Socket.prototype.remoteAddress = ""; +Socket.prototype.remoteFamily = ""; +Socket.prototype.remotePort = 0; + +Socket.prototype.address = function address() { +}; + +Socket.prototype.connect = function connect(options, callback) { + if (callback !== null) { + this.once('connect', cb); + } + + this.connecting = true; + this.writable = true; +}; + +Socket.prototype.address = function address() { +}; + +Socket.prototype.destroy = function destroy(exception) { + this.connecting = false; + this.readable = false; + this.writable = false; +}; + +Socket.prototype.end = function end(data, encoding) { +}; + +Socket.prototype.pause = function pause() { +}; + +Socket.prototype.ref = function ref() { +}; + +Socket.prototype.resume = function resume() { +}; + +Socket.prototype.setEncoding = function setEncoding(encoding) { +}; + +Socket.prototype.setKeepAlive = function setKeepAlive(enable, initialDelay) { +}; + +Socket.prototype.setNoDelay = function setNoDelay(noDelay) { +}; + +Socket.prototype.setTimeout = function setTimeout(msecs, callback) { + this.timeout = msecs; + + if (callback) { + this.on('timeout', callback); + } + + return this; +}; + +Socket.prototype.unref = function unref() { +}; + +Socket.prototype.write = function write(data, encoding, callback) { +}; + + +module.exports = { + Socket +}; diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp new file mode 100644 index 00000000..40f641a6 --- /dev/null +++ b/src/nodejs/unit-http/unit.cpp @@ -0,0 +1,905 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#include "unit.h" + + +napi_ref Unit::constructor_; + + +Unit::Unit(napi_env env): + env_(env), + wrapper_(nullptr), + unit_ctx_(nullptr) +{ +} + + +Unit::~Unit() +{ + napi_delete_reference(env_, wrapper_); +} + + +napi_value +Unit::init(napi_env env, napi_value exports) +{ + napi_value cons, fn; + napi_status status; + + napi_property_descriptor properties[] = { + { "createServer", 0, create_server, 0, 0, 0, napi_default, 0 }, + { "listen", 0, listen, 0, 0, 0, napi_default, 0 } + }; + + status = napi_define_class(env, "Unit", NAPI_AUTO_LENGTH, create, nullptr, + 2, properties, &cons); + if (status != napi_ok) { + goto failed; + } + + status = napi_create_reference(env, cons, 1, &constructor_); + if (status != napi_ok) { + goto failed; + } + + status = napi_set_named_property(env, exports, "Unit", cons); + if (status != napi_ok) { + goto failed; + } + + status = napi_create_function(env, NULL, 0, response_send_headers, NULL, + &fn); + if (status != napi_ok) { + goto failed; + } + + status = napi_set_named_property(env, exports, + "unit_response_headers", fn); + if (status != napi_ok) { + goto failed; + } + + status = napi_create_function(env, NULL, 0, response_write, NULL, &fn); + if (status != napi_ok) { + goto failed; + } + + status = napi_set_named_property(env, exports, "unit_response_write", fn); + if (status != napi_ok) { + goto failed; + } + + status = napi_create_function(env, NULL, 0, response_end, NULL, &fn); + if (status != napi_ok) { + goto failed; + } + + status = napi_set_named_property(env, exports, "unit_response_end", fn); + if (status != napi_ok) { + goto failed; + } + + return exports; + +failed: + + napi_throw_error(env, NULL, "Failed to define Unit class"); + + return nullptr; +} + + +void +Unit::destroy(napi_env env, void *nativeObject, void *finalize_hint) +{ + Unit *obj = reinterpret_cast(nativeObject); + + delete obj; +} + + +napi_value +Unit::create(napi_env env, napi_callback_info info) +{ + Unit *obj; + napi_value target, cons, instance, jsthis; + napi_status status; + + status = napi_get_new_target(env, info, &target); + if (status != napi_ok) { + goto failed; + } + + if (target != nullptr) { + /* Invoked as constructor: `new Unit(...)` */ + status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, + nullptr); + if (status != napi_ok) { + goto failed; + } + + obj = new Unit(env); + + status = napi_wrap(env, jsthis, reinterpret_cast(obj), + destroy, nullptr, &obj->wrapper_); + if (status != napi_ok) { + goto failed; + } + + return jsthis; + } + + /* Invoked as plain function `Unit(...)`, turn into construct call. */ + status = napi_get_reference_value(env, constructor_, &cons); + if (status != napi_ok) { + goto failed; + } + + status = napi_new_instance(env, cons, 0, nullptr, &instance); + if (status != napi_ok) { + goto failed; + } + + return instance; + +failed: + + napi_throw_error(env, NULL, "Failed to create Unit object"); + + return nullptr; +} + + +napi_value +Unit::create_server(napi_env env, napi_callback_info info) +{ + Unit *obj; + size_t argc; + napi_value jsthis; + napi_status status; + napi_value argv[1]; + nxt_unit_init_t unit_init; + + argc = 1; + + status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr); + if (status != napi_ok) { + goto failed; + } + + status = napi_unwrap(env, jsthis, reinterpret_cast(&obj)); + if (status != napi_ok) { + goto failed; + } + + memset(&unit_init, 0, sizeof(nxt_unit_init_t)); + + unit_init.data = obj; + unit_init.callbacks.request_handler = request_handler; + + obj->unit_ctx_ = nxt_unit_init(&unit_init); + if (obj->unit_ctx_ == NULL) { + goto failed; + } + + return nullptr; + +failed: + + napi_throw_error(env, NULL, "Failed to create Unit object"); + + return nullptr; +} + + +napi_value +Unit::listen(napi_env env, napi_callback_info info) +{ + int ret; + Unit *obj; + napi_value jsthis; + napi_status status; + + status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr); + if (status != napi_ok) { + goto failed; + } + + status = napi_unwrap(env, jsthis, reinterpret_cast(&obj)); + if (status != napi_ok) { + goto failed; + } + + if (obj->unit_ctx_ == NULL) { + napi_throw_error(env, NULL, "Unit context was not created"); + return nullptr; + } + + ret = nxt_unit_run(obj->unit_ctx_); + if (ret != NXT_UNIT_OK) { + napi_throw_error(env, NULL, "Failed to run Unit"); + return nullptr; + } + + nxt_unit_done(obj->unit_ctx_); + + return nullptr; + +failed: + + napi_throw_error(env, NULL, "Failed to listen Unit socket"); + + return nullptr; +} + + +void +Unit::request_handler(nxt_unit_request_info_t *req) +{ + Unit *obj; + napi_value socket, request, response; + napi_value global, server_obj; + napi_value req_argv[3]; + napi_status status; + + obj = reinterpret_cast(req->unit->data); + + napi_handle_scope scope; + status = napi_open_handle_scope(obj->env_, &scope); + if (status != napi_ok) { + napi_throw_error(obj->env_, NULL, "Failed to create handle scope"); + return; + } + + server_obj = obj->get_server_object(); + if (server_obj == nullptr) { + napi_throw_error(obj->env_, NULL, "Failed to get server object"); + return; + } + + status = napi_get_global(obj->env_, &global); + if (status != napi_ok) { + napi_throw_error(obj->env_, NULL, "Failed to get global variable"); + return; + } + + socket = obj->create_socket(server_obj, req); + if (socket == nullptr) { + napi_throw_error(obj->env_, NULL, "Failed to create socket object"); + return; + } + + request = obj->create_request(server_obj, socket); + if (request == nullptr) { + napi_throw_error(obj->env_, NULL, "Failed to create request object"); + return; + } + + response = obj->create_response(server_obj, socket, request, req, obj); + if (response == nullptr) { + napi_throw_error(obj->env_, NULL, "Failed to create response object"); + return; + } + + req_argv[1] = request; + req_argv[2] = response; + + status = obj->create_headers(req, request); + if (status != napi_ok) { + napi_throw_error(obj->env_, NULL, "Failed to create headers"); + return; + } + + obj->emit(server_obj, "request", sizeof("request") - 1, 3, req_argv); + obj->emit_post_data(request, req); + + napi_close_handle_scope(obj->env_, scope); +} + + +napi_value +Unit::get_server_object() +{ + napi_value unit_obj, server_obj; + napi_status status; + + status = napi_get_reference_value(env_, wrapper_, &unit_obj); + if (status != napi_ok) { + return nullptr; + } + + status = napi_get_named_property(env_, unit_obj, "server", &server_obj); + if (status != napi_ok) { + return nullptr; + } + + return server_obj; +} + + +napi_value +Unit::emit(napi_value obj, const char *name, size_t name_len, size_t argc, + napi_value *argv) +{ + napi_value emitter, return_val, str; + napi_status status; + + status = napi_get_named_property(env_, obj, "emit", &emitter); + if (status != napi_ok) { + return nullptr; + } + + status = napi_create_string_latin1(env_, name, name_len, &str); + if (status != napi_ok) { + return nullptr; + } + + if (argc != 0) { + argv[0] = str; + + } else { + argc = 1; + argv = &str; + } + + status = napi_call_function(env_, obj, emitter, argc, argv, &return_val); + if (status != napi_ok) { + return nullptr; + } + + return return_val; +} + + +napi_status +Unit::create_headers(nxt_unit_request_info_t *req, napi_value request) +{ + uint32_t i; + const char *p; + napi_value headers, raw_headers, str; + napi_status status; + nxt_unit_field_t *f; + nxt_unit_request_t *r; + + r = req->request; + + status = napi_create_object(env_, &headers); + if (status != napi_ok) { + return status; + } + + status = napi_create_array_with_length(env_, r->fields_count * 2, + &raw_headers); + if (status != napi_ok) { + return status; + } + + for (i = 0; i < r->fields_count; i++) { + f = r->fields + i; + + status = this->append_header(f, headers, raw_headers, i); + if (status != napi_ok) { + return status; + } + } + + status = napi_set_named_property(env_, request, "headers", headers); + if (status != napi_ok) { + return status; + } + + status = napi_set_named_property(env_, request, "raw_headers", raw_headers); + if (status != napi_ok) { + return status; + } + + p = (const char *) nxt_unit_sptr_get(&r->version); + + status = napi_create_string_latin1(env_, p, r->version_length, &str); + if (status != napi_ok) { + return status; + } + + status = napi_set_named_property(env_, request, "httpVersion", str); + if (status != napi_ok) { + return status; + } + + p = (const char *) nxt_unit_sptr_get(&r->method); + + status = napi_create_string_latin1(env_, p, r->method_length, &str); + if (status != napi_ok) { + return status; + } + + status = napi_set_named_property(env_, request, "method", str); + if (status != napi_ok) { + return status; + } + + p = (const char *) nxt_unit_sptr_get(&r->target); + + status = napi_create_string_latin1(env_, p, r->target_length, &str); + if (status != napi_ok) { + return status; + } + + status = napi_set_named_property(env_, request, "url", str); + if (status != napi_ok) { + return status; + } + + return napi_ok; +} + + +inline napi_status +Unit::append_header(nxt_unit_field_t *f, napi_value headers, + napi_value raw_headers, uint32_t idx) +{ + const char *name, *value; + napi_value str, vstr; + napi_status status; + + value = (const char *) nxt_unit_sptr_get(&f->value); + + status = napi_create_string_latin1(env_, value, f->value_length, &vstr); + if (status != napi_ok) { + return status; + } + + name = (const char *) nxt_unit_sptr_get(&f->name); + + status = napi_set_named_property(env_, headers, name, vstr); + if (status != napi_ok) { + return status; + } + + status = napi_create_string_latin1(env_, name, f->name_length, &str); + if (status != napi_ok) { + return status; + } + + status = napi_set_element(env_, raw_headers, idx * 2, str); + if (status != napi_ok) { + return status; + } + + status = napi_set_element(env_, raw_headers, idx * 2 + 1, vstr); + if (status != napi_ok) { + return status; + } + + return napi_ok; +} + + +napi_value +Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req) +{ + napi_value constructor, return_val; + napi_status status; + + status = napi_get_named_property(env_, server_obj, "socket", + &constructor); + if (status != napi_ok) { + return nullptr; + } + + status = napi_new_instance(env_, constructor, 0, NULL, &return_val); + if (status != napi_ok) { + return nullptr; + } + + return return_val; +} + + +napi_value +Unit::create_request(napi_value server_obj, napi_value socket) +{ + napi_value constructor, return_val; + napi_status status; + + status = napi_get_named_property(env_, server_obj, "request", + &constructor); + if (status != napi_ok) { + return nullptr; + } + + status = napi_new_instance(env_, constructor, 1, &server_obj, + &return_val); + if (status != napi_ok) { + return nullptr; + } + + status = napi_set_named_property(env_, return_val, "socket", socket); + if (status != napi_ok) { + return nullptr; + } + + return return_val; +} + + +napi_value +Unit::create_response(napi_value server_obj, napi_value socket, + napi_value request, nxt_unit_request_info_t *req, + Unit *obj) +{ + napi_value constructor, return_val, req_num; + napi_status status; + + status = napi_get_named_property(env_, server_obj, "response", + &constructor); + if (status != napi_ok) { + return nullptr; + } + + status = napi_new_instance(env_, constructor, 1, &request, &return_val); + if (status != napi_ok) { + return nullptr; + } + + status = napi_set_named_property(env_, return_val, "socket", socket); + if (status != napi_ok) { + return nullptr; + } + + status = napi_create_int64(env_, (int64_t) (uintptr_t) req, &req_num); + if (status != napi_ok) { + return nullptr; + } + + status = napi_set_named_property(env_, return_val, "_req_point", req_num); + if (status != napi_ok) { + return nullptr; + } + + return return_val; +} + + +void +Unit::emit_post_data(napi_value request, nxt_unit_request_info_t *req) +{ + void *data; + napi_value req_argv[2]; + napi_status status; + + status = napi_create_buffer(env_, (size_t) req->content_length, + &data, &req_argv[1]); + if (status != napi_ok) { + napi_throw_error(env_, NULL, "Failed to create request buffer"); + return; + } + + nxt_unit_request_read(req, data, req->content_length); + + emit(request, "data", sizeof("data") - 1, 2, req_argv); + emit(request, "end", sizeof("end") - 1, 0, nullptr); +} + + +napi_value +Unit::response_send_headers(napi_env env, napi_callback_info info) +{ + int ret; + char *ptr, *name_ptr; + bool is_array; + size_t argc, name_len, value_len; + int64_t req_p; + uint32_t status_code, header_len, keys_len, array_len; + uint32_t keys_count, i, j; + uint16_t hash; + napi_value this_arg, headers, keys, name, value, array_val; + napi_value req_num; + napi_status status; + nxt_unit_field_t *f; + nxt_unit_request_info_t *req; + napi_value argv[5]; + + argc = 5; + + status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL); + if (status != napi_ok) { + return nullptr; + } + + if (argc != 5) { + napi_throw_error(env, NULL, "Wrong args count. Need three: " + "statusCode, headers, headers count, headers length"); + return nullptr; + } + + status = napi_get_named_property(env, argv[0], "_req_point", &req_num); + if (status != napi_ok) { + napi_throw_error(env, NULL, "Failed to get request pointer"); + return nullptr; + } + + status = napi_get_value_int64(env, req_num, &req_p); + if (status != napi_ok) { + napi_throw_error(env, NULL, "Failed to get request pointer"); + return nullptr; + } + + req = (nxt_unit_request_info_t *) (uintptr_t) req_p; + + status = napi_get_value_uint32(env, argv[1], &status_code); + if (status != napi_ok) { + goto failed; + } + + status = napi_get_value_uint32(env, argv[3], &keys_count); + if (status != napi_ok) { + goto failed; + } + + status = napi_get_value_uint32(env, argv[4], &header_len); + if (status != napi_ok) { + goto failed; + } + + /* Need to reserve extra byte for C-string 0-termination. */ + header_len++; + + headers = argv[2]; + + ret = nxt_unit_response_init(req, status_code, keys_count, header_len); + if (ret != NXT_UNIT_OK) { + goto failed; + } + + status = napi_get_property_names(env, headers, &keys); + if (status != napi_ok) { + goto failed; + } + + status = napi_get_array_length(env, keys, &keys_len); + if (status != napi_ok) { + goto failed; + } + + ptr = req->response_buf->free; + + for (i = 0; i < keys_len; i++) { + status = napi_get_element(env, keys, i, &name); + if (status != napi_ok) { + goto failed; + } + + status = napi_get_property(env, headers, name, &value); + if (status != napi_ok) { + goto failed; + } + + status = napi_get_value_string_latin1(env, name, ptr, header_len, + &name_len); + if (status != napi_ok) { + goto failed; + } + + name_ptr = ptr; + + ptr += name_len; + header_len -= name_len; + + hash = nxt_unit_field_hash(name_ptr, name_len); + + status = napi_is_array(env, value, &is_array); + if (status != napi_ok) { + goto failed; + } + + if (is_array) { + status = napi_get_array_length(env, value, &array_len); + if (status != napi_ok) { + goto failed; + } + + for (j = 0; j < array_len; j++) { + status = napi_get_element(env, value, j, &array_val); + if (status != napi_ok) { + goto failed; + } + + status = napi_get_value_string_latin1(env, array_val, ptr, + header_len, + &value_len); + if (status != napi_ok) { + goto failed; + } + + f = req->response->fields + req->response->fields_count; + f->skip = 0; + + nxt_unit_sptr_set(&f->name, name_ptr); + + f->name_length = name_len; + f->hash = hash; + + nxt_unit_sptr_set(&f->value, ptr); + f->value_length = (uint32_t) value_len; + + ptr += value_len; + header_len -= value_len; + + req->response->fields_count++; + } + + } else { + status = napi_get_value_string_latin1(env, value, ptr, header_len, + &value_len); + if (status != napi_ok) { + goto failed; + } + + f = req->response->fields + req->response->fields_count; + f->skip = 0; + + nxt_unit_sptr_set(&f->name, name_ptr); + + f->name_length = name_len; + f->hash = hash; + + nxt_unit_sptr_set(&f->value, ptr); + f->value_length = (uint32_t) value_len; + + ptr += value_len; + header_len -= value_len; + + req->response->fields_count++; + } + } + + req->response_buf->free = ptr; + + ret = nxt_unit_response_send(req); + if (ret != NXT_UNIT_OK) { + goto failed; + } + + return this_arg; + +failed: + + req->response->fields_count = 0; + + napi_throw_error(env, NULL, "Failed to write headers"); + + return nullptr; +} + + +napi_value +Unit::response_write(napi_env env, napi_callback_info info) +{ + int ret; + char *ptr; + size_t argc, have_buf_len; + int64_t req_p; + uint32_t buf_len; + napi_value this_arg, req_num; + napi_status status; + nxt_unit_buf_t *buf; + napi_valuetype buf_type; + nxt_unit_request_info_t *req; + napi_value argv[3]; + + argc = 3; + + status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL); + if (status != napi_ok) { + goto failed; + } + + if (argc != 3) { + napi_throw_error(env, NULL, "Wrong args count. Need two: " + "chunk, chunk length"); + return nullptr; + } + + status = napi_get_named_property(env, argv[0], "_req_point", &req_num); + if (status != napi_ok) { + napi_throw_error(env, NULL, "Failed to get request pointer"); + return nullptr; + } + + status = napi_get_value_int64(env, req_num, &req_p); + if (status != napi_ok) { + napi_throw_error(env, NULL, "Failed to get request pointer"); + return nullptr; + } + + req = (nxt_unit_request_info_t *) (uintptr_t) req_p; + + status = napi_get_value_uint32(env, argv[2], &buf_len); + if (status != napi_ok) { + goto failed; + } + + status = napi_typeof(env, argv[1], &buf_type); + if (status != napi_ok) { + goto failed; + } + + buf_len++; + + buf = nxt_unit_response_buf_alloc(req, buf_len); + if (buf == NULL) { + goto failed; + } + + if (buf_type == napi_string) { + /* TODO: will work only for utf8 content-type */ + + status = napi_get_value_string_utf8(env, argv[1], buf->free, + buf_len, &have_buf_len); + + } else { + status = napi_get_buffer_info(env, argv[1], (void **) &ptr, + &have_buf_len); + + memcpy(buf->free, ptr, have_buf_len); + } + + if (status != napi_ok) { + goto failed; + } + + buf->free += have_buf_len; + + ret = nxt_unit_buf_send(buf); + if (ret != NXT_UNIT_OK) { + goto failed; + } + + return this_arg; + +failed: + + napi_throw_error(env, NULL, "Failed to write body"); + + return nullptr; +} + + +napi_value +Unit::response_end(napi_env env, napi_callback_info info) +{ + size_t argc; + int64_t req_p; + napi_value resp, this_arg, req_num; + napi_status status; + nxt_unit_request_info_t *req; + + argc = 1; + + status = napi_get_cb_info(env, info, &argc, &resp, &this_arg, NULL); + if (status != napi_ok) { + napi_throw_error(env, NULL, "Failed to finalize sending body"); + return nullptr; + } + + status = napi_get_named_property(env, resp, "_req_point", &req_num); + if (status != napi_ok) { + napi_throw_error(env, NULL, "Failed to get request pointer"); + return nullptr; + } + + status = napi_get_value_int64(env, req_num, &req_p); + if (status != napi_ok) { + napi_throw_error(env, NULL, "Failed to get request pointer"); + return nullptr; + } + + req = (nxt_unit_request_info_t *) (uintptr_t) req_p; + + nxt_unit_request_done(req, NXT_UNIT_OK); + + return this_arg; +} diff --git a/src/nodejs/unit-http/unit.h b/src/nodejs/unit-http/unit.h new file mode 100644 index 00000000..753a14d8 --- /dev/null +++ b/src/nodejs/unit-http/unit.h @@ -0,0 +1,77 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_NODEJS_UNIT_H_INCLUDED_ +#define _NXT_NODEJS_UNIT_H_INCLUDED_ + + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +class Unit { +public: + static napi_value init(napi_env env, napi_value exports); + +private: + Unit(napi_env env); + ~Unit(); + + static napi_value create(napi_env env, napi_callback_info info); + static void destroy(napi_env env, void *nativeObject, void *finalize_hint); + + static napi_value create_server(napi_env env, napi_callback_info info); + static napi_value listen(napi_env env, napi_callback_info info); + static void request_handler(nxt_unit_request_info_t *req); + + napi_value get_server_object(); + + napi_value emit(napi_value obj, const char *name, size_t name_len, + size_t argc, napi_value *argv); + + napi_value create_socket(napi_value server_obj, + nxt_unit_request_info_t *req); + + napi_value create_request(napi_value server_obj, napi_value socket); + + napi_value create_response(napi_value server_obj, napi_value socket, + napi_value request, + nxt_unit_request_info_t *req, Unit *obj); + + void emit_post_data(napi_value request, nxt_unit_request_info_t *req); + + static napi_value response_send_headers(napi_env env, + napi_callback_info info); + + static napi_value response_write(napi_env env, napi_callback_info info); + static napi_value response_end(napi_env env, napi_callback_info info); + + napi_status create_headers(nxt_unit_request_info_t *req, + napi_value request); + + inline napi_status append_header(nxt_unit_field_t *f, napi_value headers, + napi_value raw_headers, uint32_t idx); + + static napi_ref constructor_; + + napi_env env_; + napi_ref wrapper_; + nxt_unit_ctx_t *unit_ctx_; +}; + + +#endif /* _NXT_NODEJS_H_INCLUDED_ */ -- cgit From 029c1a9f509b2af60e02d74ef982fda1346d85e0 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Tue, 9 Oct 2018 17:53:31 +0300 Subject: Renamed "go" application type to "external". There's nothing specific to Go language. This type of application object can be used to run any external application that utilizes libunit API. --- src/nxt_application.c | 8 +-- src/nxt_application.h | 26 +++---- src/nxt_conf_validation.c | 32 ++++----- src/nxt_external.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++ src/nxt_go.c | 175 --------------------------------------------- src/nxt_main_process.c | 42 +++++------ src/nxt_router.c | 2 +- src/nxt_runtime.c | 4 +- 8 files changed, 235 insertions(+), 232 deletions(-) create mode 100644 src/nxt_external.c delete mode 100644 src/nxt_go.c (limited to 'src') diff --git a/src/nxt_application.c b/src/nxt_application.c index 3c62f7d4..acdebe04 100644 --- a/src/nxt_application.c +++ b/src/nxt_application.c @@ -507,15 +507,15 @@ nxt_app_parse_type(u_char *p, size_t length) str.length = length; str.start = p; - if (nxt_str_eq(&str, "python", 6)) { + if (nxt_str_eq(&str, "external", 8) || nxt_str_eq(&str, "go", 2)) { + return NXT_APP_EXTERNAL; + + } else if (nxt_str_eq(&str, "python", 6)) { return NXT_APP_PYTHON; } else if (nxt_str_eq(&str, "php", 3)) { return NXT_APP_PHP; - } else if (nxt_str_eq(&str, "go", 2)) { - return NXT_APP_GO; - } else if (nxt_str_eq(&str, "perl", 4)) { return NXT_APP_PERL; diff --git a/src/nxt_application.h b/src/nxt_application.h index 35346655..10f5a922 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -15,9 +15,9 @@ typedef enum { + NXT_APP_EXTERNAL, NXT_APP_PYTHON, NXT_APP_PHP, - NXT_APP_GO, NXT_APP_PERL, NXT_APP_RUBY, @@ -39,6 +39,12 @@ typedef struct { typedef struct nxt_common_app_conf_s nxt_common_app_conf_t; +typedef struct { + char *executable; + nxt_conf_value_t *arguments; +} nxt_external_app_conf_t; + + typedef struct { char *home; nxt_str_t path; @@ -54,12 +60,6 @@ typedef struct { } nxt_php_app_conf_t; -typedef struct { - char *executable; - nxt_conf_value_t *arguments; -} nxt_go_app_conf_t; - - typedef struct { char *script; } nxt_perl_app_conf_t; @@ -80,11 +80,11 @@ struct nxt_common_app_conf_s { nxt_conf_value_t *environment; union { - nxt_python_app_conf_t python; - nxt_php_app_conf_t php; - nxt_go_app_conf_t go; - nxt_perl_app_conf_t perl; - nxt_ruby_app_conf_t ruby; + nxt_external_app_conf_t external; + nxt_python_app_conf_t python; + nxt_php_app_conf_t php; + nxt_perl_app_conf_t perl; + nxt_ruby_app_conf_t ruby; } u; }; @@ -161,7 +161,7 @@ nxt_app_lang_module_t *nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name); nxt_app_type_t nxt_app_parse_type(u_char *p, size_t length); NXT_EXPORT extern nxt_str_t nxt_server; -extern nxt_app_module_t nxt_go_module; +extern nxt_app_module_t nxt_external_module; NXT_EXPORT nxt_int_t nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init); diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index ca7107cc..b1e30955 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -256,6 +256,21 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[] = { }; +static nxt_conf_vldt_object_t nxt_conf_vldt_external_members[] = { + { nxt_string("executable"), + NXT_CONF_VLDT_STRING, + NULL, + NULL }, + + { nxt_string("arguments"), + NXT_CONF_VLDT_ARRAY, + &nxt_conf_vldt_array_iterator, + (void *) &nxt_conf_vldt_argument }, + + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) +}; + + static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { { nxt_string("home"), NXT_CONF_VLDT_STRING, @@ -321,21 +336,6 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { }; -static nxt_conf_vldt_object_t nxt_conf_vldt_go_members[] = { - { nxt_string("executable"), - NXT_CONF_VLDT_STRING, - NULL, - NULL }, - - { nxt_string("arguments"), - NXT_CONF_VLDT_ARRAY, - &nxt_conf_vldt_array_iterator, - (void *) &nxt_conf_vldt_argument }, - - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) -}; - - static nxt_conf_vldt_object_t nxt_conf_vldt_perl_members[] = { { nxt_string("script"), NXT_CONF_VLDT_STRING, @@ -565,9 +565,9 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name, static nxt_str_t type_str = nxt_string("type"); static void *members[] = { + nxt_conf_vldt_external_members, nxt_conf_vldt_python_members, nxt_conf_vldt_php_members, - nxt_conf_vldt_go_members, nxt_conf_vldt_perl_members, nxt_conf_vldt_ruby_members, }; diff --git a/src/nxt_external.c b/src/nxt_external.c new file mode 100644 index 00000000..c7aacffc --- /dev/null +++ b/src/nxt_external.c @@ -0,0 +1,178 @@ + +/* + * Copyright (C) Max Romanov + * Copyright (C) NGINX, Inc. + */ + +#include +#include +#include + + +static nxt_int_t nxt_external_init(nxt_task_t *task, + nxt_common_app_conf_t *conf); + + +nxt_app_module_t nxt_external_module = { + 0, + NULL, + nxt_string("external"), + "*", + nxt_external_init, +}; + + +extern char **environ; + + +nxt_inline nxt_int_t +nxt_external_fd_no_cloexec(nxt_task_t *task, nxt_socket_t fd) +{ + int res, flags; + + if (fd == -1) { + return NXT_OK; + } + + flags = fcntl(fd, F_GETFD); + + if (nxt_slow_path(flags == -1)) { + nxt_alert(task, "fcntl(%d, F_GETFD) failed %E", fd, nxt_errno); + return NXT_ERROR; + } + + flags &= ~FD_CLOEXEC; + + res = fcntl(fd, F_SETFD, flags); + + if (nxt_slow_path(res == -1)) { + nxt_alert(task, "fcntl(%d, F_SETFD) failed %E", fd, nxt_errno); + return NXT_ERROR; + } + + nxt_fd_blocking(task, fd); + + return NXT_OK; +} + + +static nxt_int_t +nxt_external_init(nxt_task_t *task, nxt_common_app_conf_t *conf) +{ + char **argv; + u_char buf[256]; + u_char *p, *end; + uint32_t index; + size_t size; + nxt_str_t str; + nxt_int_t rc; + nxt_uint_t i, argc; + nxt_port_t *my_port, *main_port; + nxt_runtime_t *rt; + nxt_conf_value_t *value; + nxt_external_app_conf_t *c; + + rt = task->thread->runtime; + + main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + my_port = nxt_runtime_port_find(rt, nxt_pid, 0); + + if (nxt_slow_path(main_port == NULL || my_port == NULL)) { + return NXT_ERROR; + } + + rc = nxt_external_fd_no_cloexec(task, main_port->pair[1]); + if (nxt_slow_path(rc != NXT_OK)) { + return NXT_ERROR; + } + + rc = nxt_external_fd_no_cloexec(task, my_port->pair[0]); + if (nxt_slow_path(rc != NXT_OK)) { + return NXT_ERROR; + } + + end = buf + sizeof(buf); + + p = nxt_sprintf(buf, end, + "%s;%uD;" + "%PI,%ud,%d;" + "%PI,%ud,%d;" + "%d,%Z", + NXT_VERSION, my_port->process->init->stream, + main_port->pid, main_port->id, main_port->pair[1], + my_port->pid, my_port->id, my_port->pair[0], + 2); + + if (nxt_slow_path(p == end)) { + nxt_alert(task, "internal error: buffer too small for NXT_UNIT_INIT"); + + return NXT_ERROR; + } + + nxt_debug(task, "update "NXT_UNIT_INIT_ENV"=%s", buf); + + rc = setenv(NXT_UNIT_INIT_ENV, (char *) buf, 1); + if (nxt_slow_path(rc == -1)) { + nxt_alert(task, "setenv("NXT_UNIT_INIT_ENV", %s) failed %E", buf, + nxt_errno); + + return NXT_ERROR; + } + + c = &conf->u.external; + + argc = 2; + size = 0; + + if (c->arguments != NULL) { + + for (index = 0; /* void */ ; index++) { + value = nxt_conf_get_array_element(c->arguments, index); + if (value == NULL) { + break; + } + + nxt_conf_get_string(value, &str); + + size += str.length + 1; + argc++; + } + } + + argv = nxt_malloc(argc * sizeof(argv[0]) + size); + if (nxt_slow_path(argv == NULL)) { + nxt_alert(task, "failed to allocate arguments"); + return NXT_ERROR; + } + + argv[0] = c->executable; + i = 1; + + if (c->arguments != NULL) { + p = (u_char *) &argv[argc]; + + for (index = 0; /* void */ ; index++) { + value = nxt_conf_get_array_element(c->arguments, index); + if (value == NULL) { + break; + } + + argv[i++] = (char *) p; + + nxt_conf_get_string(value, &str); + + p = nxt_cpymem(p, str.start, str.length); + *p++ = '\0'; + } + } + + argv[i] = NULL; + + (void) execve(c->executable, argv, environ); + + nxt_alert(task, "execve(%s) failed %E", c->executable, nxt_errno); + + nxt_free(argv); + + return NXT_ERROR; +} diff --git a/src/nxt_go.c b/src/nxt_go.c deleted file mode 100644 index 4e7d0488..00000000 --- a/src/nxt_go.c +++ /dev/null @@ -1,175 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#include -#include -#include - - -static nxt_int_t nxt_go_init(nxt_task_t *task, nxt_common_app_conf_t *conf); - -nxt_app_module_t nxt_go_module = { - 0, - NULL, - nxt_string("go"), - "*", - nxt_go_init, -}; - - -extern char **environ; - -nxt_inline nxt_int_t -nxt_go_fd_no_cloexec(nxt_task_t *task, nxt_socket_t fd) -{ - int res, flags; - - if (fd == -1) { - return NXT_OK; - } - - flags = fcntl(fd, F_GETFD); - - if (nxt_slow_path(flags == -1)) { - nxt_alert(task, "fcntl(%d, F_GETFD) failed %E", fd, nxt_errno); - return NXT_ERROR; - } - - flags &= ~FD_CLOEXEC; - - res = fcntl(fd, F_SETFD, flags); - - if (nxt_slow_path(res == -1)) { - nxt_alert(task, "fcntl(%d, F_SETFD) failed %E", fd, nxt_errno); - return NXT_ERROR; - } - - nxt_fd_blocking(task, fd); - - return NXT_OK; -} - - -static nxt_int_t -nxt_go_init(nxt_task_t *task, nxt_common_app_conf_t *conf) -{ - char **argv; - u_char buf[256]; - u_char *p, *end; - uint32_t index; - size_t size; - nxt_str_t str; - nxt_int_t rc; - nxt_uint_t i, argc; - nxt_port_t *my_port, *main_port; - nxt_runtime_t *rt; - nxt_conf_value_t *value; - nxt_go_app_conf_t *c; - - rt = task->thread->runtime; - - main_port = rt->port_by_type[NXT_PROCESS_MAIN]; - my_port = nxt_runtime_port_find(rt, nxt_pid, 0); - - if (nxt_slow_path(main_port == NULL || my_port == NULL)) { - return NXT_ERROR; - } - - rc = nxt_go_fd_no_cloexec(task, main_port->pair[1]); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } - - rc = nxt_go_fd_no_cloexec(task, my_port->pair[0]); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } - - end = buf + sizeof(buf); - - p = nxt_sprintf(buf, end, - "%s;%uD;" - "%PI,%ud,%d;" - "%PI,%ud,%d;" - "%d,%Z", - NXT_VERSION, my_port->process->init->stream, - main_port->pid, main_port->id, main_port->pair[1], - my_port->pid, my_port->id, my_port->pair[0], - 2); - - if (nxt_slow_path(p == end)) { - nxt_alert(task, "internal error: buffer too small for NXT_UNIT_INIT"); - - return NXT_ERROR; - } - - nxt_debug(task, "update "NXT_UNIT_INIT_ENV"=%s", buf); - - rc = setenv(NXT_UNIT_INIT_ENV, (char *) buf, 1); - if (nxt_slow_path(rc == -1)) { - nxt_alert(task, "setenv("NXT_UNIT_INIT_ENV", %s) failed %E", buf, - nxt_errno); - - return NXT_ERROR; - } - - c = &conf->u.go; - - argc = 2; - size = 0; - - if (c->arguments != NULL) { - - for (index = 0; /* void */ ; index++) { - value = nxt_conf_get_array_element(c->arguments, index); - if (value == NULL) { - break; - } - - nxt_conf_get_string(value, &str); - - size += str.length + 1; - argc++; - } - } - - argv = nxt_malloc(argc * sizeof(argv[0]) + size); - if (nxt_slow_path(argv == NULL)) { - nxt_alert(task, "failed to allocate arguments"); - return NXT_ERROR; - } - - argv[0] = c->executable; - i = 1; - - if (c->arguments != NULL) { - p = (u_char *) &argv[argc]; - - for (index = 0; /* void */ ; index++) { - value = nxt_conf_get_array_element(c->arguments, index); - if (value == NULL) { - break; - } - - argv[i++] = (char *) p; - - nxt_conf_get_string(value, &str); - - p = nxt_cpymem(p, str.start, str.length); - *p++ = '\0'; - } - } - - argv[i] = NULL; - - (void) execve(c->executable, argv, environ); - - nxt_alert(task, "execve(%s) failed %E", c->executable, nxt_errno); - - nxt_free(argv); - - return NXT_ERROR; -} diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 491b09f3..819ed44c 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -134,6 +134,22 @@ static nxt_conf_map_t nxt_common_app_conf[] = { }; +static nxt_conf_map_t nxt_external_app_conf[] = { + { + nxt_string("executable"), + NXT_CONF_MAP_CSTRZ, + offsetof(nxt_common_app_conf_t, u.external.executable), + }, + + { + nxt_string("arguments"), + NXT_CONF_MAP_PTR, + offsetof(nxt_common_app_conf_t, u.external.arguments), + }, + +}; + + static nxt_conf_map_t nxt_python_app_conf[] = { { nxt_string("home"), @@ -182,22 +198,6 @@ static nxt_conf_map_t nxt_php_app_conf[] = { }; -static nxt_conf_map_t nxt_go_app_conf[] = { - { - nxt_string("executable"), - NXT_CONF_MAP_CSTRZ, - offsetof(nxt_common_app_conf_t, u.go.executable), - }, - - { - nxt_string("arguments"), - NXT_CONF_MAP_PTR, - offsetof(nxt_common_app_conf_t, u.go.arguments), - }, - -}; - - static nxt_conf_map_t nxt_perl_app_conf[] = { { nxt_string("script"), @@ -217,11 +217,11 @@ static nxt_conf_map_t nxt_ruby_app_conf[] = { static nxt_conf_app_map_t nxt_app_maps[] = { - { nxt_nitems(nxt_python_app_conf), nxt_python_app_conf }, - { nxt_nitems(nxt_php_app_conf), nxt_php_app_conf }, - { nxt_nitems(nxt_go_app_conf), nxt_go_app_conf }, - { nxt_nitems(nxt_perl_app_conf), nxt_perl_app_conf }, - { nxt_nitems(nxt_ruby_app_conf), nxt_ruby_app_conf }, + { nxt_nitems(nxt_external_app_conf), nxt_external_app_conf }, + { nxt_nitems(nxt_python_app_conf), nxt_python_app_conf }, + { nxt_nitems(nxt_php_app_conf), nxt_php_app_conf }, + { nxt_nitems(nxt_perl_app_conf), nxt_perl_app_conf }, + { nxt_nitems(nxt_ruby_app_conf), nxt_ruby_app_conf }, }; diff --git a/src/nxt_router.c b/src/nxt_router.c index 139b2c4c..3a5382e4 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -289,9 +289,9 @@ static const nxt_str_t http_prefix = nxt_string("HTTP_"); static const nxt_str_t empty_prefix = nxt_string(""); static const nxt_str_t *nxt_app_msg_prefix[] = { + &empty_prefix, &http_prefix, &http_prefix, - &empty_prefix, &http_prefix, &http_prefix, }; diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index 311c2c08..8f20e0a0 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -80,10 +80,10 @@ nxt_runtime_create(nxt_task_t *task) /* Should not fail. */ lang = nxt_array_add(rt->languages); - lang->type = NXT_APP_GO; + lang->type = NXT_APP_EXTERNAL; lang->version = (u_char *) ""; lang->file = NULL; - lang->module = &nxt_go_module; + lang->module = &nxt_external_module; listen_sockets = nxt_array_create(mp, 1, sizeof(nxt_listen_socket_t)); if (nxt_slow_path(listen_sockets == NULL)) { -- cgit From 161e1839a5e3d9d906c047c2464a239a632d42c2 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 22 Oct 2018 16:16:05 +0300 Subject: Compatibility with LibreSSL. LibreSSL uses high OPENSSL_VERSION_NUMBER, but has no SSL_CTX_add0_chain_cert(). --- src/nxt_openssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index 91ba3cb0..441da54b 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -404,7 +404,7 @@ nxt_openssl_chain_file(SSL_CTX *ctx, nxt_fd_t fd) * while the main certificate needs a X509_free() call, since * its reference count is increased by SSL_CTX_use_certificate(). */ -#if OPENSSL_VERSION_NUMBER > 0x10002000L +#ifdef SSL_CTX_add0_chain_cert if (SSL_CTX_add0_chain_cert(ctx, ca) != 1) { #else if (SSL_CTX_add_extra_chain_cert(ctx, ca) != 1) { -- cgit From a4aaf906794d9f82710614ba368a36500e8a8254 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Mon, 22 Oct 2018 16:01:10 +0300 Subject: Re-engineered timers. To optimize rbtree operations, all changes are stored in array and later processed in batches. The previous implementation of this mechanics had a number of design flaws. Each change was saved in a new array entry; until the changes were applied, the timer remained in an intermediate state (NXT_TIMER_CHANGING). This intermediate state didn't allow to identify if time was going to be disabled or enabled. However, the nxt_conn_io_read() function relied on this information; as a result, in some cases the read timeout wasn't set. Also, the nxt_timer_delete() function did not reliably track whether a timer was added to the work queue. It checked the NXT_TIMER_ENQUEUED state of a timer, but this state could be reset to NXT_TIMER_DISABLED by a nxt_timer_disable() call or another nxt_timer_delete() call. Now, instead of keeping the whole history of the timer's changes, the new implementation updates the timer state immediately, and only one operation is added to the array to add or delete timer in the rbtree according to its final state. --- src/nxt_conn_read.c | 4 +- src/nxt_timer.c | 135 +++++++++++++++++++++------------------------------- src/nxt_timer.h | 36 +++++++++----- 3 files changed, 78 insertions(+), 97 deletions(-) (limited to 'src') diff --git a/src/nxt_conn_read.c b/src/nxt_conn_read.c index 21086233..e458bf81 100644 --- a/src/nxt_conn_read.c +++ b/src/nxt_conn_read.c @@ -105,7 +105,7 @@ nxt_conn_io_read(nxt_task_t *task, void *obj, void *data) * occured during read operation, it toggled write event * internally so only read timer should be set. */ - if (c->read_timer.state == NXT_TIMER_DISABLED) { + if (!c->read_timer.enabled) { nxt_conn_timer(engine, c, state, &c->read_timer); } @@ -117,7 +117,7 @@ nxt_conn_io_read(nxt_task_t *task, void *obj, void *data) nxt_fd_event_enable_read(engine, &c->socket); } - if (state->timer_autoreset || c->read_timer.state == NXT_TIMER_DISABLED) { + if (state->timer_autoreset || !c->read_timer.enabled) { nxt_conn_timer(engine, c, state, &c->read_timer); } } diff --git a/src/nxt_timer.c b/src/nxt_timer.c index f56ddeb6..97807d75 100644 --- a/src/nxt_timer.c +++ b/src/nxt_timer.c @@ -32,6 +32,10 @@ nxt_timers_init(nxt_timers_t *timers, nxt_uint_t mchanges) { nxt_rbtree_init(&timers->tree, nxt_timer_rbtree_compare); + if (mchanges > NXT_TIMER_MAX_CHANGES) { + mchanges = NXT_TIMER_MAX_CHANGES; + } + timers->mchanges = mchanges; timers->changes = nxt_malloc(sizeof(nxt_timer_change_t) * mchanges); @@ -71,66 +75,47 @@ nxt_timer_add(nxt_event_engine_t *engine, nxt_timer_t *timer, time = engine->timers.now + timeout; - if (timer->state != NXT_TIMER_CHANGING) { + nxt_debug(timer->task, "timer add: %M %M:%M", timer->time, timeout, time); - if (nxt_timer_is_in_tree(timer)) { + timer->enabled = 1; - diff = nxt_msec_diff(time, timer->time); - /* - * Use the previous timer if difference between it and the - * new timer is less than required precision milliseconds: this - * decreases number of rbtree operations for fast connections. - */ - if (nxt_abs(diff) < timer->precision) { - nxt_debug(timer->task, "timer previous: %M:%d", - time, timer->state); + if (nxt_timer_is_in_tree(timer)) { - timer->state = NXT_TIMER_WAITING; - return; - } + diff = nxt_msec_diff(time, timer->time); + /* + * Use the previous timer if difference between it and the + * new timer is less than required precision milliseconds: this + * decreases number of rbtree operations for fast connections. + */ + if (nxt_abs(diff) < timer->precision) { + nxt_debug(timer->task, "timer previous: %M", time); + + nxt_timer_change(engine, timer, NXT_TIMER_NOPE, 0); + return; } } - nxt_debug(timer->task, "timer add: %M:%d %M:%M", - timer->time, timer->state, timeout, time); - nxt_timer_change(engine, timer, NXT_TIMER_ADD, time); } -void -nxt_timer_disable(nxt_event_engine_t *engine, nxt_timer_t *timer) -{ - nxt_debug(timer->task, "timer disable: %M:%d", timer->time, timer->state); - - if (timer->state != NXT_TIMER_CHANGING) { - timer->state = NXT_TIMER_DISABLED; - - } else { - nxt_timer_change(engine, timer, NXT_TIMER_DISABLE, 0); - } -} - - nxt_bool_t nxt_timer_delete(nxt_event_engine_t *engine, nxt_timer_t *timer) { - nxt_bool_t pending; + nxt_debug(timer->task, "timer delete: %M", timer->time); + + timer->enabled = 0; - if (nxt_timer_is_in_tree(timer) || timer->state == NXT_TIMER_CHANGING) { - nxt_debug(timer->task, "timer delete: %M:%d", - timer->time, timer->state); + if (nxt_timer_is_in_tree(timer)) { nxt_timer_change(engine, timer, NXT_TIMER_DELETE, 0); return 1; } - pending = (timer->state == NXT_TIMER_ENQUEUED); - - timer->state = NXT_TIMER_DISABLED; + nxt_timer_change(engine, timer, NXT_TIMER_NOPE, 0); - return pending; + return (timer->queued || timer->change != NXT_TIMER_NO_CHANGE); } @@ -143,31 +128,35 @@ nxt_timer_change(nxt_event_engine_t *engine, nxt_timer_t *timer, timers = &engine->timers; - if (timers->nchanges >= timers->mchanges) { - nxt_timer_changes_commit(engine); + if (timer->change == NXT_TIMER_NO_CHANGE) { + + if (change == NXT_TIMER_NOPE) { + return; + } + + if (timers->nchanges >= timers->mchanges) { + nxt_timer_changes_commit(engine); + } + + timers->nchanges++; + timer->change = timers->nchanges; } nxt_debug(timer->task, "timer change: %M:%d", time, change); - timer->state = NXT_TIMER_CHANGING; - - ch = &timers->changes[timers->nchanges]; + ch = &timers->changes[timer->change - 1]; ch->change = change; ch->time = time; ch->timer = timer; - - timers->nchanges++; } static void nxt_timer_changes_commit(nxt_event_engine_t *engine) { - int32_t diff; nxt_timer_t *timer; nxt_timers_t *timers; - nxt_timer_state_t state; nxt_timer_change_t *ch, *end; timers = &engine->timers; @@ -178,28 +167,16 @@ nxt_timer_changes_commit(nxt_event_engine_t *engine) end = ch + timers->nchanges; while (ch < end) { - state = NXT_TIMER_DISABLED; - timer = ch->timer; switch (ch->change) { + case NXT_TIMER_NOPE: + break; + case NXT_TIMER_ADD: if (nxt_timer_is_in_tree(timer)) { - - diff = nxt_msec_diff(ch->time, timer->time); - /* See the comment in nxt_timer_add(). */ - - if (nxt_abs(diff) < timer->precision) { - nxt_debug(timer->task, "timer rbtree previous: %M:%d", - ch->time, timer->state); - - state = NXT_TIMER_WAITING; - break; - } - - nxt_debug(timer->task, "timer rbtree delete: %M:%d", - timer->time, timer->state); + nxt_debug(timer->task, "timer rbtree delete: %M", timer->time); nxt_rbtree_delete(&timers->tree, &timer->node); } @@ -210,26 +187,19 @@ nxt_timer_changes_commit(nxt_event_engine_t *engine) nxt_rbtree_insert(&timers->tree, &timer->node); nxt_timer_in_tree_set(timer); - state = NXT_TIMER_WAITING; break; case NXT_TIMER_DELETE: - if (nxt_timer_is_in_tree(timer)) { - nxt_debug(timer->task, "timer rbtree delete: %M:%d", - timer->time, timer->state); + nxt_debug(timer->task, "timer rbtree delete: %M", timer->time); - nxt_rbtree_delete(&timers->tree, &timer->node); - nxt_timer_in_tree_clear(timer); - } + nxt_rbtree_delete(&timers->tree, &timer->node); + nxt_timer_in_tree_clear(timer); break; - - case NXT_TIMER_DISABLE: - break; } - timer->state = state; + timer->change = NXT_TIMER_NO_CHANGE; ch++; } @@ -270,7 +240,7 @@ nxt_timer_find(nxt_event_engine_t *engine) * return much earlier and the disabled timer can be reactivated. */ - if (timer->state != NXT_TIMER_DISABLED) { + if (timer->enabled) { time = timer->time; timers->minimum = time; @@ -324,14 +294,13 @@ nxt_timer_expire(nxt_event_engine_t *engine, nxt_msec_t now) next = nxt_rbtree_node_successor(tree, node); - nxt_debug(timer->task, "timer expire delete: %M:%d", - timer->time, timer->state); + nxt_debug(timer->task, "timer expire delete: %M", timer->time); nxt_rbtree_delete(tree, &timer->node); nxt_timer_in_tree_clear(timer); - if (timer->state != NXT_TIMER_DISABLED) { - timer->state = NXT_TIMER_ENQUEUED; + if (timer->enabled) { + timer->queued = 1; nxt_work_queue_add(timer->work_queue, nxt_timer_handler, timer->task, timer, NULL); @@ -347,8 +316,10 @@ nxt_timer_handler(nxt_task_t *task, void *obj, void *data) timer = obj; - if (timer->state == NXT_TIMER_ENQUEUED) { - timer->state = NXT_TIMER_DISABLED; + timer->queued = 0; + + if (timer->enabled && timer->change == NXT_TIMER_NO_CHANGE) { + timer->enabled = 0; timer->handler(task, timer, NULL); } diff --git a/src/nxt_timer.h b/src/nxt_timer.h index 6531657d..211564d6 100644 --- a/src/nxt_timer.h +++ b/src/nxt_timer.h @@ -13,20 +13,24 @@ //#define NXT_TIMER_DEFAULT_PRECISION 1 -typedef enum { - NXT_TIMER_DISABLED = 0, - NXT_TIMER_CHANGING, - NXT_TIMER_WAITING, - NXT_TIMER_ENQUEUED, -} nxt_timer_state_t; +/* + * The nxt_timer_t structure can hold up to 14 bits of change index, + * but 0 reserved for NXT_TIMER_NO_CHANGE. + */ +#define NXT_TIMER_MAX_CHANGES 16383 +#define NXT_TIMER_NO_CHANGE 0 typedef struct { /* The rbtree node must be the first field. */ NXT_RBTREE_NODE (node); - nxt_timer_state_t state:8; uint8_t precision; + + uint16_t change:14; + uint16_t enabled:1; + uint16_t queued:1; + nxt_msec_t time; nxt_work_queue_t *work_queue; @@ -37,13 +41,13 @@ typedef struct { } nxt_timer_t; -#define NXT_TIMER { NXT_RBTREE_NODE_INIT, NXT_TIMER_DISABLED, \ - 0, 0, NULL, NULL, NULL, NULL } +#define NXT_TIMER { NXT_RBTREE_NODE_INIT, 0, NXT_TIMER_NO_CHANGE, \ + 0, 0, 0, NULL, NULL, NULL, NULL } typedef enum { - NXT_TIMER_ADD = 0, - NXT_TIMER_DISABLE, + NXT_TIMER_NOPE = 0, + NXT_TIMER_ADD, NXT_TIMER_DELETE, } nxt_timer_operation_t; @@ -94,10 +98,16 @@ void nxt_timer_expire(nxt_event_engine_t *engine, nxt_msec_t now); NXT_EXPORT void nxt_timer_add(nxt_event_engine_t *engine, nxt_timer_t *timer, nxt_msec_t timeout); -NXT_EXPORT void nxt_timer_disable(nxt_event_engine_t *engine, - nxt_timer_t *timer); NXT_EXPORT nxt_bool_t nxt_timer_delete(nxt_event_engine_t *engine, nxt_timer_t *timer); +nxt_inline void +nxt_timer_disable(nxt_event_engine_t *engine, nxt_timer_t *timer) +{ + nxt_debug(timer->task, "timer disable: %M", timer->time); + + timer->enabled = 0; +} + #endif /* _NXT_TIMER_H_INCLUDED_ */ -- cgit From b20e995e80d236945ec8388dfee37257ce9e5445 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Mon, 22 Oct 2018 16:02:14 +0300 Subject: Timers: separation of delete and insert operations on rbtree. Delete/insert operation complexity for a red-black tree is O(log n), where n is the total number of tree elements. If all delete operations are performed before all insert operations, the average number of tree elements during an operation will be lower than in the mixed-operations case. --- src/nxt_timer.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nxt_timer.c b/src/nxt_timer.c index 97807d75..0b89dafc 100644 --- a/src/nxt_timer.c +++ b/src/nxt_timer.c @@ -155,7 +155,7 @@ nxt_timer_change(nxt_event_engine_t *engine, nxt_timer_t *timer, static void nxt_timer_changes_commit(nxt_event_engine_t *engine) { - nxt_timer_t *timer; + nxt_timer_t *timer, **add, **add_end; nxt_timers_t *timers; nxt_timer_change_t *ch, *end; @@ -166,6 +166,9 @@ nxt_timer_changes_commit(nxt_event_engine_t *engine) ch = timers->changes; end = ch + timers->nchanges; + add = (nxt_timer_t **) ch; + add_end = add; + while (ch < end) { timer = ch->timer; @@ -175,20 +178,16 @@ nxt_timer_changes_commit(nxt_event_engine_t *engine) break; case NXT_TIMER_ADD: - if (nxt_timer_is_in_tree(timer)) { - nxt_debug(timer->task, "timer rbtree delete: %M", timer->time); - - nxt_rbtree_delete(&timers->tree, &timer->node); - } timer->time = ch->time; - nxt_debug(timer->task, "timer rbtree insert: %M", timer->time); + *add_end++ = timer; - nxt_rbtree_insert(&timers->tree, &timer->node); - nxt_timer_in_tree_set(timer); + if (!nxt_timer_is_in_tree(timer)) { + break; + } - break; + /* Fall through. */ case NXT_TIMER_DELETE: nxt_debug(timer->task, "timer rbtree delete: %M", timer->time); @@ -204,6 +203,17 @@ nxt_timer_changes_commit(nxt_event_engine_t *engine) ch++; } + while (add < add_end) { + timer = *add; + + nxt_debug(timer->task, "timer rbtree insert: %M", timer->time); + + nxt_rbtree_insert(&timers->tree, &timer->node); + nxt_timer_in_tree_set(timer); + + add++; + } + timers->nchanges = 0; } -- cgit From da0ef366dc1affa740618eb4f4baa8bbb62f8d70 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Mon, 22 Oct 2018 16:04:16 +0300 Subject: Handling of timers with bias. Timers that don't require maximum precision (most of them, actually) can be triggered earlier or later within the bias interval. To reduce wakeups by timers, the expire function now triggers not only all timers that fall within the elapsed time, but also those whose bias falls within this interval. --- src/nxt_conn.h | 2 +- src/nxt_http_request.c | 2 +- src/nxt_router.c | 4 ++-- src/nxt_timer.c | 37 ++++++++++++++++++++++--------------- src/nxt_timer.h | 8 ++++---- 5 files changed, 30 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/nxt_conn.h b/src/nxt_conn.h index 6f6cae9c..d8b48694 100644 --- a/src/nxt_conn.h +++ b/src/nxt_conn.h @@ -175,7 +175,7 @@ struct nxt_conn_s { do { \ (ev)->work_queue = (wq); \ (ev)->log = &(c)->log; \ - (ev)->precision = NXT_TIMER_DEFAULT_PRECISION; \ + (ev)->bias = NXT_TIMER_DEFAULT_BIAS; \ } while (0) diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 06d45247..b80998cb 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -192,7 +192,7 @@ nxt_http_app_request(nxt_task_t *task, void *obj, void *data) ar->timer.task = &engine->task; ar->timer.work_queue = &engine->fast_work_queue; ar->timer.log = engine->task.log; - ar->timer.precision = NXT_TIMER_DEFAULT_PRECISION; + ar->timer.bias = NXT_TIMER_DEFAULT_BIAS; ar->r.remote.start = nxt_sockaddr_address(r->remote); ar->r.remote.length = r->remote->address_length; diff --git a/src/nxt_router.c b/src/nxt_router.c index 3a5382e4..7ecbca81 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -1578,7 +1578,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, app_joint->use_count = 1; app_joint->app = app; - app_joint->idle_timer.precision = NXT_TIMER_DEFAULT_PRECISION; + app_joint->idle_timer.bias = NXT_TIMER_DEFAULT_BIAS; app_joint->idle_timer.work_queue = &engine->fast_work_queue; app_joint->idle_timer.handler = nxt_router_app_idle_timeout; app_joint->idle_timer.task = &engine->task; @@ -4119,7 +4119,7 @@ nxt_router_adjust_idle_timer(nxt_task_t *task, void *obj, void *data) nxt_assert(app->engine == engine); - threshold = engine->timers.now + app->joint->idle_timer.precision; + threshold = engine->timers.now + app->joint->idle_timer.bias; timeout = 0; nxt_thread_mutex_lock(&app->mutex); diff --git a/src/nxt_timer.c b/src/nxt_timer.c index 0b89dafc..cba4755b 100644 --- a/src/nxt_timer.c +++ b/src/nxt_timer.c @@ -75,7 +75,8 @@ nxt_timer_add(nxt_event_engine_t *engine, nxt_timer_t *timer, time = engine->timers.now + timeout; - nxt_debug(timer->task, "timer add: %M %M:%M", timer->time, timeout, time); + nxt_debug(timer->task, "timer add: %M±%d %M:%M", + timer->time, timer->bias, timeout, time); timer->enabled = 1; @@ -84,11 +85,12 @@ nxt_timer_add(nxt_event_engine_t *engine, nxt_timer_t *timer, diff = nxt_msec_diff(time, timer->time); /* * Use the previous timer if difference between it and the - * new timer is less than required precision milliseconds: this - * decreases number of rbtree operations for fast connections. + * new timer is within bias: this decreases number of rbtree + * operations for fast connections. */ - if (nxt_abs(diff) < timer->precision) { - nxt_debug(timer->task, "timer previous: %M", time); + if (nxt_abs(diff) <= timer->bias) { + nxt_debug(timer->task, "timer previous: %M±%d", + time, timer->bias); nxt_timer_change(engine, timer, NXT_TIMER_NOPE, 0); return; @@ -102,7 +104,8 @@ nxt_timer_add(nxt_event_engine_t *engine, nxt_timer_t *timer, nxt_bool_t nxt_timer_delete(nxt_event_engine_t *engine, nxt_timer_t *timer) { - nxt_debug(timer->task, "timer delete: %M", timer->time); + nxt_debug(timer->task, "timer delete: %M±%d", + timer->time, timer->bias); timer->enabled = 0; @@ -142,7 +145,8 @@ nxt_timer_change(nxt_event_engine_t *engine, nxt_timer_t *timer, timer->change = timers->nchanges; } - nxt_debug(timer->task, "timer change: %M:%d", time, change); + nxt_debug(timer->task, "timer change: %M±%d:%d", + time, timer->bias, change); ch = &timers->changes[timer->change - 1]; @@ -190,7 +194,8 @@ nxt_timer_changes_commit(nxt_event_engine_t *engine) /* Fall through. */ case NXT_TIMER_DELETE: - nxt_debug(timer->task, "timer rbtree delete: %M", timer->time); + nxt_debug(timer->task, "timer rbtree delete: %M±%d", + timer->time, timer->bias); nxt_rbtree_delete(&timers->tree, &timer->node); nxt_timer_in_tree_clear(timer); @@ -206,7 +211,8 @@ nxt_timer_changes_commit(nxt_event_engine_t *engine) while (add < add_end) { timer = *add; - nxt_debug(timer->task, "timer rbtree insert: %M", timer->time); + nxt_debug(timer->task, "timer rbtree insert: %M±%d", + timer->time, timer->bias); nxt_rbtree_insert(&timers->tree, &timer->node); nxt_timer_in_tree_set(timer); @@ -252,10 +258,10 @@ nxt_timer_find(nxt_event_engine_t *engine) if (timer->enabled) { time = timer->time; - timers->minimum = time; + timers->minimum = time - timer->bias; - nxt_debug(timer->task, "timer found minimum: %M:%M", - time, timers->now); + nxt_debug(timer->task, "timer found minimum: %M±%d:%M", + time, timer->bias, timers->now); delta = nxt_msec_diff(time, timers->now); @@ -297,14 +303,15 @@ nxt_timer_expire(nxt_event_engine_t *engine, nxt_msec_t now) { timer = (nxt_timer_t *) node; - /* timer->time > now */ - if (nxt_msec_diff(timer->time , now) > 0) { + /* timer->time > now + timer->bias */ + if (nxt_msec_diff(timer->time , now) > (int32_t) timer->bias) { return; } next = nxt_rbtree_node_successor(tree, node); - nxt_debug(timer->task, "timer expire delete: %M", timer->time); + nxt_debug(timer->task, "timer expire delete: %M±%d", + timer->time, timer->bias); nxt_rbtree_delete(tree, &timer->node); nxt_timer_in_tree_clear(timer); diff --git a/src/nxt_timer.h b/src/nxt_timer.h index 211564d6..4199f0dd 100644 --- a/src/nxt_timer.h +++ b/src/nxt_timer.h @@ -8,9 +8,9 @@ #define _NXT_TIMER_H_INCLUDED_ -/* Valid values are between 1ms to 255ms. */ -#define NXT_TIMER_DEFAULT_PRECISION 100 -//#define NXT_TIMER_DEFAULT_PRECISION 1 +/* Valid values are between 0ms to 255ms. */ +#define NXT_TIMER_DEFAULT_BIAS 50 +//#define NXT_TIMER_DEFAULT_BIAS 0 /* @@ -25,7 +25,7 @@ typedef struct { /* The rbtree node must be the first field. */ NXT_RBTREE_NODE (node); - uint8_t precision; + uint8_t bias; uint16_t change:14; uint16_t enabled:1; -- cgit From deeb276d2386b90e695fc0650bfb7e08806264ee Mon Sep 17 00:00:00 2001 From: Igor Sysoev Date: Mon, 22 Oct 2018 16:25:58 +0300 Subject: Removed duplicate code in epoll. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit is related to #173 issue on GitHub. Thanks to 洪志道 (Hong Zhi Dao). --- src/nxt_epoll_engine.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'src') diff --git a/src/nxt_epoll_engine.c b/src/nxt_epoll_engine.c index 68e8d609..bc606d7a 100644 --- a/src/nxt_epoll_engine.c +++ b/src/nxt_epoll_engine.c @@ -942,8 +942,6 @@ nxt_epoll_poll(nxt_event_engine_t *engine, nxt_msec_t timeout) ev->read = NXT_EVENT_DISABLED; } - error = 0; - nxt_work_queue_add(ev->read_work_queue, ev->read_handler, ev->task, ev, ev->data); @@ -962,8 +960,6 @@ nxt_epoll_poll(nxt_event_engine_t *engine, nxt_msec_t timeout) ev->write = NXT_EVENT_DISABLED; } - error = 0; - nxt_work_queue_add(ev->write_work_queue, ev->write_handler, ev->task, ev, ev->data); @@ -972,11 +968,6 @@ nxt_epoll_poll(nxt_event_engine_t *engine, nxt_msec_t timeout) nxt_epoll_disable_write(engine, ev); } } - - if (error) { - ev->read_ready = 1; - ev->write_ready = 1; - } } } -- cgit From 50dd54877dc88ade87f4259a74918ddfa4498880 Mon Sep 17 00:00:00 2001 From: Igor Sysoev Date: Mon, 22 Oct 2018 17:43:28 +0300 Subject: Improved epoll failures handling. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit epoll changes are committed to the kernel before epoll_wait() or on changes array overflow. In the latter case if there are errors epoll_wait() timeout was not set to zero. This commit is related to #173 issue on GitHub. Thanks to 洪志道 (Hong Zhi Dao). --- src/nxt_epoll_engine.c | 23 +++++++++++------------ src/nxt_event_engine.h | 2 ++ 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/nxt_epoll_engine.c b/src/nxt_epoll_engine.c index bc606d7a..d87b92cf 100644 --- a/src/nxt_epoll_engine.c +++ b/src/nxt_epoll_engine.c @@ -67,7 +67,7 @@ static void nxt_epoll_enable_accept(nxt_event_engine_t *engine, nxt_fd_event_t *ev); static void nxt_epoll_change(nxt_event_engine_t *engine, nxt_fd_event_t *ev, int op, uint32_t events); -static nxt_int_t nxt_epoll_commit_changes(nxt_event_engine_t *engine); +static void nxt_epoll_commit_changes(nxt_event_engine_t *engine); static void nxt_epoll_error_handler(nxt_task_t *task, void *obj, void *data); #if (NXT_HAVE_SIGNALFD) static nxt_int_t nxt_epoll_add_signal(nxt_event_engine_t *engine); @@ -593,7 +593,7 @@ nxt_epoll_change(nxt_event_engine_t *engine, nxt_fd_event_t *ev, int op, engine->u.epoll.fd, ev->fd, op, events); if (engine->u.epoll.nchanges >= engine->u.epoll.mchanges) { - (void) nxt_epoll_commit_changes(engine); + nxt_epoll_commit_changes(engine); } ev->changing = 1; @@ -605,18 +605,16 @@ nxt_epoll_change(nxt_event_engine_t *engine, nxt_fd_event_t *ev, int op, } -static nxt_int_t +static void nxt_epoll_commit_changes(nxt_event_engine_t *engine) { int ret; - nxt_int_t retval; nxt_fd_event_t *ev; nxt_epoll_change_t *change, *end; nxt_debug(&engine->task, "epoll %d changes:%ui", engine->u.epoll.fd, engine->u.epoll.nchanges); - retval = NXT_OK; change = engine->u.epoll.changes; end = change + engine->u.epoll.nchanges; @@ -637,7 +635,7 @@ nxt_epoll_commit_changes(nxt_event_engine_t *engine) nxt_work_queue_add(&engine->fast_work_queue, nxt_epoll_error_handler, ev->task, ev, ev->data); - retval = NXT_ERROR; + engine->u.epoll.error = 1; } change++; @@ -645,8 +643,6 @@ nxt_epoll_commit_changes(nxt_event_engine_t *engine) } while (change < end); engine->u.epoll.nchanges = 0; - - return retval; } @@ -884,10 +880,13 @@ nxt_epoll_poll(nxt_event_engine_t *engine, nxt_msec_t timeout) struct epoll_event *event; if (engine->u.epoll.nchanges != 0) { - if (nxt_epoll_commit_changes(engine) != NXT_OK) { - /* Error handlers have been enqueued on failure. */ - timeout = 0; - } + nxt_epoll_commit_changes(engine); + } + + if (engine->u.epoll.error) { + engine->u.epoll.error = 0; + /* Error handlers have been enqueued on failure. */ + timeout = 0; } nxt_debug(&engine->task, "epoll_wait(%d) timeout:%M", diff --git a/src/nxt_event_engine.h b/src/nxt_event_engine.h index 7a7149e6..365d9e89 100644 --- a/src/nxt_event_engine.h +++ b/src/nxt_event_engine.h @@ -202,6 +202,8 @@ typedef struct { nxt_uint_t mchanges; int mevents; + uint8_t error; /* 1 bit */ + nxt_epoll_change_t *changes; struct epoll_event *events; -- cgit From 57991dc3f2ee1a028605e5dde01cac367e09142c Mon Sep 17 00:00:00 2001 From: Igor Sysoev Date: Mon, 22 Oct 2018 17:43:31 +0300 Subject: Fixed termination signal handlers in worker processes. This closes #99 issue on GitHub. Thanks to Julian Brost. --- src/nxt_worker_process.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nxt_worker_process.c b/src/nxt_worker_process.c index 8bcfb467..c068cca4 100644 --- a/src/nxt_worker_process.c +++ b/src/nxt_worker_process.c @@ -42,8 +42,8 @@ nxt_port_handlers_t nxt_discovery_process_port_handlers = { const nxt_sig_event_t nxt_worker_process_signals[] = { nxt_event_signal(SIGHUP, nxt_worker_process_signal_handler), nxt_event_signal(SIGINT, nxt_worker_process_sigterm_handler), - nxt_event_signal(SIGQUIT, nxt_worker_process_sigterm_handler), - nxt_event_signal(SIGTERM, nxt_worker_process_sigquit_handler), + nxt_event_signal(SIGQUIT, nxt_worker_process_sigquit_handler), + nxt_event_signal(SIGTERM, nxt_worker_process_sigterm_handler), nxt_event_signal(SIGCHLD, nxt_worker_process_signal_handler), nxt_event_signal(SIGUSR1, nxt_worker_process_signal_handler), nxt_event_signal(SIGUSR2, nxt_worker_process_signal_handler), -- cgit From 780181412623c0dc9232fc7f56055b6e3312a319 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Tue, 23 Oct 2018 13:06:44 +0300 Subject: Added ability to publish Node.js module. --- src/nodejs/unit-http/README.md | 19 +++++++++++++++++++ src/nodejs/unit-http/binding_pub.gyp | 2 +- src/nodejs/unit-http/package.json | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nodejs/unit-http/README.md b/src/nodejs/unit-http/README.md index 139597f9..71a4067a 100644 --- a/src/nodejs/unit-http/README.md +++ b/src/nodejs/unit-http/README.md @@ -1,2 +1,21 @@ +# Node.js Package for NGINX Unit +[](https://unit.nginx.org) +Node.js support package for NGINX Unit. +For details, see [NGINX Unit documentation](https://unit.nginx.org). +## Installation + +```bash +npm i unit-http +``` + +## Usage + +```javascript +var http = require('unit-http'); +``` + +## License + +Apache 2.0 diff --git a/src/nodejs/unit-http/binding_pub.gyp b/src/nodejs/unit-http/binding_pub.gyp index e53538d0..6fe3d9bc 100644 --- a/src/nodejs/unit-http/binding_pub.gyp +++ b/src/nodejs/unit-http/binding_pub.gyp @@ -2,6 +2,6 @@ 'targets': [{ 'target_name': "unit-http", 'sources': ["unit.cpp", "addon.cpp"], - 'libraries': ["unit"] + 'libraries': ["-lunit"] }] } diff --git a/src/nodejs/unit-http/package.json b/src/nodejs/unit-http/package.json index bc481bc9..3a15d573 100644 --- a/src/nodejs/unit-http/package.json +++ b/src/nodejs/unit-http/package.json @@ -21,7 +21,7 @@ "install": "node-gyp configure build" }, "author": "Alexander Borisov", - "license": "Apache 2.0", + "license": "Apache-2.0", "gypfile": true, "dependencies": { "node-addon-api": "1.2.0" -- cgit From d4a99aad84e1ab432733650e9753814311e04a01 Mon Sep 17 00:00:00 2001 From: Igor Sysoev Date: Tue, 23 Oct 2018 16:31:42 +0300 Subject: Backout of ba94959b1dec and improving epoll error handling. --- src/nxt_epoll_engine.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nxt_epoll_engine.c b/src/nxt_epoll_engine.c index d87b92cf..9f9c8f62 100644 --- a/src/nxt_epoll_engine.c +++ b/src/nxt_epoll_engine.c @@ -920,8 +920,8 @@ nxt_epoll_poll(nxt_event_engine_t *engine, nxt_msec_t timeout) ev->fd, events, ev, ev->read, ev->write); /* - * On error epoll may set EPOLLERR and EPOLLHUP only without EPOLLIN or - * EPOLLOUT, so the "error" variable enqueues only one active handler. + * On error epoll may set EPOLLERR and EPOLLHUP only without EPOLLIN + * or EPOLLOUT, so the "error" variable enqueues only error handler. */ error = ((events & (EPOLLERR | EPOLLHUP)) != 0); ev->epoll_error = error; @@ -932,7 +932,7 @@ nxt_epoll_poll(nxt_event_engine_t *engine, nxt_msec_t timeout) #endif - if ((events & EPOLLIN) || error) { + if ((events & EPOLLIN) != 0) { ev->read_ready = 1; if (ev->read != NXT_EVENT_BLOCKED) { @@ -948,9 +948,11 @@ nxt_epoll_poll(nxt_event_engine_t *engine, nxt_msec_t timeout) /* Level-triggered mode. */ nxt_epoll_disable_read(engine, ev); } + + error = 0; } - if ((events & EPOLLOUT) || error) { + if ((events & EPOLLOUT) != 0) { ev->write_ready = 1; if (ev->write != NXT_EVENT_BLOCKED) { @@ -966,7 +968,29 @@ nxt_epoll_poll(nxt_event_engine_t *engine, nxt_msec_t timeout) /* Level-triggered mode. */ nxt_epoll_disable_write(engine, ev); } + + error = 0; + } + + if (!error) { + continue; + } + + ev->read_ready = 1; + ev->write_ready = 1; + + if (ev->read == NXT_EVENT_BLOCKED && ev->write == NXT_EVENT_BLOCKED) { + + if (engine->u.epoll.mode == 0) { + /* Level-triggered mode. */ + nxt_epoll_disable(engine, ev); + } + + continue; } + + nxt_work_queue_add(&engine->fast_work_queue, nxt_epoll_error_handler, + ev->task, ev, ev->data); } } -- cgit From 6cc4080af23d0d8c39ba43f6c90de4f76d64e0f1 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Tue, 23 Oct 2018 18:55:16 +0300 Subject: Removed unused "--upstream" command line option. --- src/nxt_runtime.c | 14 -------------- src/nxt_runtime.h | 1 - 2 files changed, 15 deletions(-) (limited to 'src') diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index 8f20e0a0..547c7494 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -872,20 +872,6 @@ nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt) continue; } - if (nxt_strcmp(p, "--upstream") == 0) { - if (*argv == NULL) { - nxt_alert(task, "no argument for option \"--upstream\""); - return NXT_ERROR; - } - - p = *argv++; - - rt->upstream.length = nxt_strlen(p); - rt->upstream.start = (u_char *) p; - - continue; - } - if (nxt_strcmp(p, "--user") == 0) { if (*argv == NULL) { write(STDERR_FILENO, no_user, nxt_length(no_user)); diff --git a/src/nxt_runtime.h b/src/nxt_runtime.h index e4c9e2a1..496ae478 100644 --- a/src/nxt_runtime.h +++ b/src/nxt_runtime.h @@ -74,7 +74,6 @@ struct nxt_runtime_s { nxt_sockaddr_t *controller_listen; nxt_listen_socket_t *controller_socket; - nxt_str_t upstream; }; -- cgit From 5b3d3e0a885614cbc7966bc1d4b73ba630caf1ed Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 24 Oct 2018 16:21:53 +0300 Subject: Node.js: additional type check for response.setHeader value. --- src/nodejs/unit-http/http_server.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index 8c144cbe..fa7b8e9b 100755 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -52,7 +52,7 @@ ServerResponse.prototype.setHeader = function setHeader(key, value) { throw new TypeError('Key argument must be a string'); } - let header_key_len = Buffer.byteLength(key + "", 'latin1'); + let header_key_len = Buffer.byteLength(key, 'latin1'); let header_len = 0 let header_count = 0; @@ -60,16 +60,16 @@ ServerResponse.prototype.setHeader = function setHeader(key, value) { header_count = value.length; value.forEach(function(val) { - if (typeof val !== 'string') { - throw new TypeError('Entry in arrey should be a string'); + if (typeof val !== 'string' && typeof val !== 'number') { + throw new TypeError('Array entries must be string or number'); } header_len += Buffer.byteLength(val + "", 'latin1'); }); } else { - if (typeof value !== 'string') { - throw new TypeError('Value argument must be a string or array'); + if (typeof value !== 'string' && typeof value !== 'number') { + throw new TypeError('Value argument must be string, number, or array'); } header_count = 1; @@ -78,7 +78,7 @@ ServerResponse.prototype.setHeader = function setHeader(key, value) { this.removeHeader(key); - this.headers[key] = value; + this.headers[key] = value + ""; this.headers_len += header_len + (header_key_len * header_count); this.headers_count += header_count; }; -- cgit