summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nodejs/unit-http/http.js15
-rw-r--r--src/nodejs/unit-http/http_server.js24
-rw-r--r--src/nodejs/unit-http/loader.js27
-rw-r--r--src/nodejs/unit-http/loader.mjs18
-rw-r--r--src/nxt_application.h1
-rw-r--r--src/nxt_cert.c19
-rw-r--r--src/nxt_conf_validation.c284
-rw-r--r--src/nxt_errno.h1
-rw-r--r--src/nxt_file.c44
-rw-r--r--src/nxt_file.h32
-rwxr-xr-x[-rw-r--r--]src/nxt_h1proto.c0
-rw-r--r--src/nxt_http.h20
-rw-r--r--src/nxt_http_request.c4
-rw-r--r--src/nxt_http_route.c144
-rw-r--r--src/nxt_http_static.c178
-rw-r--r--src/nxt_main_process.c6
-rw-r--r--src/nxt_openssl.c146
-rw-r--r--src/nxt_pcre2.c2
-rw-r--r--src/nxt_php_sapi.c37
-rw-r--r--src/nxt_router.c123
-rw-r--r--src/nxt_router.h2
-rw-r--r--src/nxt_tls.h8
-rw-r--r--src/nxt_unix.h4
-rw-r--r--src/python/nxt_python.c164
-rw-r--r--src/python/nxt_python.h17
-rw-r--r--src/python/nxt_python_asgi.c37
-rw-r--r--src/python/nxt_python_asgi.h4
-rw-r--r--src/python/nxt_python_asgi_lifespan.c93
-rw-r--r--src/python/nxt_python_wsgi.c5
-rw-r--r--src/ruby/nxt_ruby_stream_io.c8
30 files changed, 1162 insertions, 305 deletions
diff --git a/src/nodejs/unit-http/http.js b/src/nodejs/unit-http/http.js
index 3a25fa2f..d298a35f 100644
--- a/src/nodejs/unit-http/http.js
+++ b/src/nodejs/unit-http/http.js
@@ -5,19 +5,22 @@
'use strict';
-const server = require('unit-http/http_server');
-
-const { Server } = server;
+const {
+ Server,
+ ServerRequest,
+ ServerResponse,
+} = require('./http_server');
function createServer (requestHandler) {
return new Server(requestHandler);
}
+const http = require("http")
module.exports = {
+ ...http,
Server,
- STATUS_CODES: server.STATUS_CODES,
createServer,
- IncomingMessage: server.ServerRequest,
- ServerResponse: server.ServerResponse
+ IncomingMessage: ServerRequest,
+ ServerResponse,
};
diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js
index e59296ae..89964ec3 100644
--- a/src/nodejs/unit-http/http_server.js
+++ b/src/nodejs/unit-http/http_server.js
@@ -444,17 +444,30 @@ Server.prototype.setTimeout = function setTimeout(msecs, callback) {
Server.prototype.listen = function (...args) {
this.unit.listen();
- const cb = args.pop();
-
- if (typeof cb === 'function') {
- this.once('listening', cb);
+ if (typeof args[args.length - 1] === 'function') {
+ this.once('listening', args[args.length - 1]);
}
- this.emit('listening');
+ /*
+ * Some express.js apps use the returned server object inside the listening
+ * callback, so we timeout the listening event to occur after this function
+ * returns.
+ */
+ setImmediate(function() {
+ this.emit('listening')
+ }.bind(this))
return this;
};
+Server.prototype.address = function () {
+ return {
+ family: "IPv4",
+ address: "127.0.0.1",
+ port: 80
+ }
+}
+
Server.prototype.emit_request = function (req, res) {
if (req._websocket_handshake && this._upgradeListenerCount > 0) {
this.emit('upgrade', req, req.socket);
@@ -530,7 +543,6 @@ function connectionListener(socket) {
}
module.exports = {
- STATUS_CODES: http.STATUS_CODES,
Server,
ServerResponse,
ServerRequest,
diff --git a/src/nodejs/unit-http/loader.js b/src/nodejs/unit-http/loader.js
new file mode 100644
index 00000000..e5aa3558
--- /dev/null
+++ b/src/nodejs/unit-http/loader.js
@@ -0,0 +1,27 @@
+// can only be ran as part of a --require param on the node process
+if (module.parent && module.parent.id === "internal/preload") {
+ const { Module } = require("module")
+
+ if (!Module.prototype.require.__unit_loader) {
+ const http = require("./http")
+ const websocket = require("./websocket")
+
+ const original = Module.prototype.require;
+
+ Module.prototype.require = function (id) {
+ switch(id) {
+ case "http":
+ case "unit-http":
+ return http
+
+ case "websocket":
+ case "unit-http/websocket":
+ return websocket
+ }
+
+ return original.apply(this, arguments);
+ }
+
+ Module.prototype.require.__unit_loader = true;
+ }
+}
diff --git a/src/nodejs/unit-http/loader.mjs b/src/nodejs/unit-http/loader.mjs
new file mode 100644
index 00000000..067d63d4
--- /dev/null
+++ b/src/nodejs/unit-http/loader.mjs
@@ -0,0 +1,18 @@
+// must be ran as part of a --loader or --experimental-loader param
+export async function resolve(specifier, context, defaultResolver) {
+ switch (specifier) {
+ case "websocket":
+ return {
+ url: new URL("./websocket.js", import.meta.url).href,
+ format: "cjs"
+ }
+
+ case "http":
+ return {
+ url: new URL("./http.js", import.meta.url).href,
+ format: "cjs"
+ }
+ }
+
+ return defaultResolver(specifier, context, defaultResolver)
+}
diff --git a/src/nxt_application.h b/src/nxt_application.h
index 632c5632..45e7fa48 100644
--- a/src/nxt_application.h
+++ b/src/nxt_application.h
@@ -54,6 +54,7 @@ typedef struct {
nxt_str_t protocol;
uint32_t threads;
uint32_t thread_stack_size;
+ nxt_conf_value_t *targets;
} nxt_python_app_conf_t;
diff --git a/src/nxt_cert.c b/src/nxt_cert.c
index 3cdb69c1..1806bc19 100644
--- a/src/nxt_cert.c
+++ b/src/nxt_cert.c
@@ -48,6 +48,7 @@ static nxt_conf_value_t *nxt_cert_name_details(nxt_mp_t *mp, X509 *x509,
nxt_bool_t issuer);
static nxt_conf_value_t *nxt_cert_alt_names_details(nxt_mp_t *mp,
STACK_OF(GENERAL_NAME) *alt_names);
+static void nxt_cert_buf_completion(nxt_task_t *task, void *obj, void *data);
static nxt_lvlhsh_t nxt_cert_info;
@@ -1073,6 +1074,9 @@ nxt_cert_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp,
goto fail;
}
+ nxt_mp_retain(mp);
+ b->completion_handler = nxt_cert_buf_completion;
+
nxt_buf_cpystr(b, name);
*b->mem.free++ = '\0';
@@ -1102,6 +1106,21 @@ fail:
}
+static void
+nxt_cert_buf_completion(nxt_task_t *task, void *obj, void *data)
+{
+ nxt_mp_t *mp;
+ nxt_buf_t *b;
+
+ b = obj;
+ mp = b->data;
+ nxt_assert(b->next == NULL);
+
+ nxt_mp_free(mp, b);
+ nxt_mp_release(mp);
+}
+
+
void
nxt_cert_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
{
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index 8c5d1ec7..06ae2847 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -75,6 +75,8 @@ static nxt_int_t nxt_conf_vldt_error(nxt_conf_validation_t *vldt,
const char *fmt, ...);
static nxt_int_t nxt_conf_vldt_var(nxt_conf_validation_t *vldt,
const char *option, nxt_str_t *value);
+nxt_inline nxt_int_t nxt_conf_vldt_unsupported(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_mtypes(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
@@ -87,6 +89,10 @@ static nxt_int_t nxt_conf_vldt_listener(nxt_conf_validation_t *vldt,
#if (NXT_TLS)
static nxt_int_t nxt_conf_vldt_certificate(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
+#if (NXT_HAVE_OPENSSL_CONF_CMD)
+static nxt_int_t nxt_conf_vldt_object_conf_commands(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+#endif
static nxt_int_t nxt_conf_vldt_certificate_element(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value);
#endif
@@ -98,6 +104,8 @@ static nxt_int_t nxt_conf_vldt_return(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_python(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_python_path_element(nxt_conf_validation_t *vldt,
@@ -154,16 +162,16 @@ static nxt_int_t nxt_conf_vldt_array_iterator(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_environment(nxt_conf_validation_t *vldt,
nxt_str_t *name, nxt_conf_value_t *value);
+static nxt_int_t nxt_conf_vldt_targets_exclusive(
+ nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_targets(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_target(nxt_conf_validation_t *vldt,
+ nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_argument(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_php(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
-static nxt_int_t nxt_conf_vldt_php_targets_exclusive(
- nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data);
-static nxt_int_t nxt_conf_vldt_php_targets(nxt_conf_validation_t *vldt,
- nxt_conf_value_t *value, void *data);
-static nxt_int_t nxt_conf_vldt_php_target(nxt_conf_validation_t *vldt,
- nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_php_option(nxt_conf_validation_t *vldt,
nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_java_classpath(nxt_conf_validation_t *vldt,
@@ -200,8 +208,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[];
#endif
static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[];
+static nxt_conf_vldt_object_t nxt_conf_vldt_python_target_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[];
+static nxt_conf_vldt_object_t nxt_conf_vldt_php_target_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[];
@@ -357,7 +367,17 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = {
{
.name = nxt_string("certificate"),
.type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ .flags = NXT_CONF_VLDT_REQUIRED,
.validator = nxt_conf_vldt_certificate,
+ }, {
+ .name = nxt_string("conf_commands"),
+ .type = NXT_CONF_VLDT_OBJECT,
+#if (NXT_HAVE_OPENSSL_CONF_CMD)
+ .validator = nxt_conf_vldt_object_conf_commands,
+#else
+ .validator = nxt_conf_vldt_unsupported,
+ .u.string = "conf_commands",
+#endif
},
NXT_CONF_VLDT_END
@@ -455,9 +475,34 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_share_action_members[] = {
.name = nxt_string("share"),
.type = NXT_CONF_VLDT_STRING,
}, {
+ .name = nxt_string("types"),
+ .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_match_patterns,
+ }, {
.name = nxt_string("fallback"),
.type = NXT_CONF_VLDT_OBJECT,
.validator = nxt_conf_vldt_action,
+ }, {
+ .name = nxt_string("chroot"),
+ .type = NXT_CONF_VLDT_STRING,
+#if !(NXT_HAVE_OPENAT2)
+ .validator = nxt_conf_vldt_unsupported,
+ .u.string = "chroot",
+#endif
+ }, {
+ .name = nxt_string("follow_symlinks"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+#if !(NXT_HAVE_OPENAT2)
+ .validator = nxt_conf_vldt_unsupported,
+ .u.string = "follow_symlinks",
+#endif
+ }, {
+ .name = nxt_string("traverse_mounts"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+#if !(NXT_HAVE_OPENAT2)
+ .validator = nxt_conf_vldt_unsupported,
+ .u.string = "traverse_mounts",
+#endif
},
NXT_CONF_VLDT_END
@@ -491,7 +536,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_external_members[] = {
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
+static nxt_conf_vldt_object_t nxt_conf_vldt_python_common_members[] = {
{
.name = nxt_string("home"),
.type = NXT_CONF_VLDT_STRING,
@@ -500,13 +545,6 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
.type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
.validator = nxt_conf_vldt_python_path,
}, {
- .name = nxt_string("module"),
- .type = NXT_CONF_VLDT_STRING,
- .flags = NXT_CONF_VLDT_REQUIRED,
- }, {
- .name = nxt_string("callable"),
- .type = NXT_CONF_VLDT_STRING,
- }, {
.name = nxt_string("protocol"),
.type = NXT_CONF_VLDT_STRING,
.validator = nxt_conf_vldt_python_protocol,
@@ -523,27 +561,77 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
};
+static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
+ {
+ .name = nxt_string("module"),
+ .type = NXT_CONF_VLDT_STRING,
+ .validator = nxt_conf_vldt_targets_exclusive,
+ .u.string = "module",
+ }, {
+ .name = nxt_string("callable"),
+ .type = NXT_CONF_VLDT_STRING,
+ .validator = nxt_conf_vldt_targets_exclusive,
+ .u.string = "callable",
+ }, {
+ .name = nxt_string("targets"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_targets,
+ .u.members = nxt_conf_vldt_python_target_members
+ },
+
+ NXT_CONF_VLDT_NEXT(nxt_conf_vldt_python_common_members)
+};
+
+
+static nxt_conf_vldt_object_t nxt_conf_vldt_python_target_members[] = {
+ {
+ .name = nxt_string("module"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ }, {
+ .name = nxt_string("callable"),
+ .type = NXT_CONF_VLDT_STRING,
+ },
+
+ NXT_CONF_VLDT_END
+};
+
+
+static nxt_conf_vldt_object_t nxt_conf_vldt_python_notargets_members[] = {
+ {
+ .name = nxt_string("module"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ }, {
+ .name = nxt_string("callable"),
+ .type = NXT_CONF_VLDT_STRING,
+ },
+
+ NXT_CONF_VLDT_NEXT(nxt_conf_vldt_python_common_members)
+};
+
static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = {
{
.name = nxt_string("root"),
.type = NXT_CONF_VLDT_ANY_TYPE,
- .validator = nxt_conf_vldt_php_targets_exclusive,
+ .validator = nxt_conf_vldt_targets_exclusive,
.u.string = "root",
}, {
.name = nxt_string("script"),
.type = NXT_CONF_VLDT_ANY_TYPE,
- .validator = nxt_conf_vldt_php_targets_exclusive,
+ .validator = nxt_conf_vldt_targets_exclusive,
.u.string = "script",
}, {
.name = nxt_string("index"),
.type = NXT_CONF_VLDT_ANY_TYPE,
- .validator = nxt_conf_vldt_php_targets_exclusive,
+ .validator = nxt_conf_vldt_targets_exclusive,
.u.string = "index",
}, {
.name = nxt_string("targets"),
.type = NXT_CONF_VLDT_OBJECT,
- .validator = nxt_conf_vldt_php_targets,
+ .validator = nxt_conf_vldt_targets,
+ .u.members = nxt_conf_vldt_php_target_members
},
NXT_CONF_VLDT_NEXT(nxt_conf_vldt_php_common_members)
@@ -1032,6 +1120,15 @@ nxt_conf_vldt_error(nxt_conf_validation_t *vldt, const char *fmt, ...)
}
+nxt_inline nxt_int_t
+nxt_conf_vldt_unsupported(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
+ void *data)
+{
+ return nxt_conf_vldt_error(vldt, "Unit is built without the \"%s\" "
+ "option support.", data);
+}
+
+
static nxt_int_t
nxt_conf_vldt_var(nxt_conf_validation_t *vldt, const char *option,
nxt_str_t *value)
@@ -1137,7 +1234,7 @@ nxt_conf_vldt_mtypes_extension(nxt_conf_validation_t *vldt,
dup_type = nxt_http_static_mtypes_hash_find(&ctx->hash, &ext);
- if (dup_type != NULL) {
+ if (dup_type->length != 0) {
return nxt_conf_vldt_error(vldt, "The \"%V\" file extension has been "
"declared for \"%V\" and \"%V\" "
"MIME types at the same time.",
@@ -1384,6 +1481,25 @@ nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
static nxt_int_t
+nxt_conf_vldt_python(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
+ void *data)
+{
+ nxt_conf_value_t *targets;
+
+ static nxt_str_t targets_str = nxt_string("targets");
+
+ targets = nxt_conf_get_object_member(value, &targets_str, NULL);
+
+ if (targets != NULL) {
+ return nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_python_members);
+ }
+
+ return nxt_conf_vldt_object(vldt, value,
+ nxt_conf_vldt_python_notargets_members);
+}
+
+
+static nxt_int_t
nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
{
@@ -1869,6 +1985,38 @@ nxt_conf_vldt_certificate_element(nxt_conf_validation_t *vldt,
return NXT_OK;
}
+
+#if (NXT_HAVE_OPENSSL_CONF_CMD)
+
+static nxt_int_t
+nxt_conf_vldt_object_conf_commands(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data)
+{
+ uint32_t index;
+ nxt_int_t ret;
+ nxt_str_t name;
+ nxt_conf_value_t *member;
+
+ index = 0;
+
+ for ( ;; ) {
+ member = nxt_conf_next_object_member(value, &name, &index);
+
+ if (member == NULL) {
+ break;
+ }
+
+ ret = nxt_conf_vldt_type(vldt, &name, member, NXT_CONF_VLDT_STRING);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+ }
+
+ return NXT_OK;
+}
+
+#endif
+
#endif
@@ -1923,7 +2071,7 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name,
} types[] = {
{ nxt_conf_vldt_object, nxt_conf_vldt_external_members },
- { nxt_conf_vldt_object, nxt_conf_vldt_python_members },
+ { nxt_conf_vldt_python, NULL },
{ nxt_conf_vldt_php, NULL },
{ nxt_conf_vldt_object, nxt_conf_vldt_perl_members },
{ nxt_conf_vldt_object, nxt_conf_vldt_ruby_members },
@@ -2250,6 +2398,57 @@ nxt_conf_vldt_environment(nxt_conf_validation_t *vldt, nxt_str_t *name,
static nxt_int_t
+nxt_conf_vldt_targets_exclusive(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data)
+{
+ return nxt_conf_vldt_error(vldt, "The \"%s\" option is mutually exclusive "
+ "with the \"targets\" object.", data);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_targets(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
+ void *data)
+{
+ nxt_int_t ret;
+ nxt_uint_t n;
+
+ n = nxt_conf_object_members_count(value);
+
+ if (n > 254) {
+ return nxt_conf_vldt_error(vldt, "The \"targets\" object must not "
+ "contain more than 254 members.");
+ }
+
+ vldt->ctx = data;
+
+ ret = nxt_conf_vldt_object_iterator(vldt, value, &nxt_conf_vldt_target);
+
+ vldt->ctx = NULL;
+
+ return ret;
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_target(nxt_conf_validation_t *vldt, nxt_str_t *name,
+ nxt_conf_value_t *value)
+{
+ if (name->length == 0) {
+ return nxt_conf_vldt_error(vldt,
+ "The target name must not be empty.");
+ }
+
+ if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
+ return nxt_conf_vldt_error(vldt, "The \"%V\" target must be "
+ "an object.", name);
+ }
+
+ return nxt_conf_vldt_object(vldt, value, vldt->ctx);
+}
+
+
+static nxt_int_t
nxt_conf_vldt_clone_namespaces(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
{
@@ -2417,51 +2616,6 @@ nxt_conf_vldt_php(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
static nxt_int_t
-nxt_conf_vldt_php_targets_exclusive(nxt_conf_validation_t *vldt,
- nxt_conf_value_t *value, void *data)
-{
- return nxt_conf_vldt_error(vldt, "The \"%s\" option is mutually exclusive "
- "with the \"targets\" object.", data);
-}
-
-
-static nxt_int_t
-nxt_conf_vldt_php_targets(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
- void *data)
-{
- nxt_uint_t n;
-
- n = nxt_conf_object_members_count(value);
-
- if (n > 254) {
- return nxt_conf_vldt_error(vldt, "The \"targets\" object must not "
- "contain more than 254 members.");
- }
-
- return nxt_conf_vldt_object_iterator(vldt, value,
- &nxt_conf_vldt_php_target);
-}
-
-
-static nxt_int_t
-nxt_conf_vldt_php_target(nxt_conf_validation_t *vldt, nxt_str_t *name,
- nxt_conf_value_t *value)
-{
- if (name->length == 0) {
- return nxt_conf_vldt_error(vldt,
- "The PHP target name must not be empty.");
- }
-
- if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
- return nxt_conf_vldt_error(vldt, "The \"%V\" PHP target must be "
- "an object.", name);
- }
-
- return nxt_conf_vldt_object(vldt, value, &nxt_conf_vldt_php_target_members);
-}
-
-
-static nxt_int_t
nxt_conf_vldt_php_option(nxt_conf_validation_t *vldt, nxt_str_t *name,
nxt_conf_value_t *value)
{
diff --git a/src/nxt_errno.h b/src/nxt_errno.h
index 40bcfa3f..ec700537 100644
--- a/src/nxt_errno.h
+++ b/src/nxt_errno.h
@@ -22,6 +22,7 @@ typedef int nxt_err_t;
#define NXT_EACCES EACCES
#define NXT_EBUSY EBUSY
#define NXT_EEXIST EEXIST
+#define NXT_ELOOP ELOOP
#define NXT_EXDEV EXDEV
#define NXT_ENOTDIR ENOTDIR
#define NXT_EISDIR EISDIR
diff --git a/src/nxt_file.c b/src/nxt_file.c
index a9595dd9..5d38d57e 100644
--- a/src/nxt_file.c
+++ b/src/nxt_file.c
@@ -42,6 +42,50 @@ nxt_file_open(nxt_task_t *task, nxt_file_t *file, nxt_uint_t mode,
}
+#if (NXT_HAVE_OPENAT2)
+
+nxt_int_t
+nxt_file_openat2(nxt_task_t *task, nxt_file_t *file, nxt_uint_t mode,
+ nxt_uint_t create, nxt_file_access_t access, nxt_fd_t dfd,
+ nxt_uint_t resolve)
+{
+ struct open_how how;
+
+ nxt_memzero(&how, sizeof(how));
+
+ /* O_NONBLOCK is to prevent blocking on FIFOs, special devices, etc. */
+ mode |= (O_NONBLOCK | create);
+
+ how.flags = mode;
+ how.mode = access;
+ how.resolve = resolve;
+
+ file->fd = syscall(SYS_openat2, dfd, file->name, &how, sizeof(how));
+
+ file->error = (file->fd == -1) ? nxt_errno : 0;
+
+#if (NXT_DEBUG)
+ nxt_thread_time_update(task->thread);
+#endif
+
+ nxt_debug(task, "openat2(%FD, \"%FN\"): %FD err:%d", dfd, file->name,
+ file->fd, file->error);
+
+ if (file->fd != -1) {
+ return NXT_OK;
+ }
+
+ if (file->log_level != 0) {
+ nxt_log(task, file->log_level, "openat2(%FD, \"%FN\") failed %E", dfd,
+ file->name, file->error);
+ }
+
+ return NXT_ERROR;
+}
+
+#endif
+
+
void
nxt_file_close(nxt_task_t *task, nxt_file_t *file)
{
diff --git a/src/nxt_file.h b/src/nxt_file.h
index 4f56e746..4846305b 100644
--- a/src/nxt_file.h
+++ b/src/nxt_file.h
@@ -109,6 +109,12 @@ typedef struct {
NXT_EXPORT nxt_int_t nxt_file_open(nxt_task_t *task, nxt_file_t *file,
nxt_uint_t mode, nxt_uint_t create, nxt_file_access_t access);
+#if (NXT_HAVE_OPENAT2)
+NXT_EXPORT nxt_int_t nxt_file_openat2(nxt_task_t *task, nxt_file_t *file,
+ nxt_uint_t mode, nxt_uint_t create, nxt_file_access_t access, nxt_fd_t dfd,
+ nxt_uint_t resolve);
+#endif
+
/* The file open access modes. */
#define NXT_FILE_RDONLY O_RDONLY
@@ -116,6 +122,32 @@ NXT_EXPORT nxt_int_t nxt_file_open(nxt_task_t *task, nxt_file_t *file,
#define NXT_FILE_RDWR O_RDWR
#define NXT_FILE_APPEND (O_WRONLY | O_APPEND)
+#if (NXT_HAVE_OPENAT2)
+
+#if defined(O_DIRECTORY)
+#define NXT_FILE_DIRECTORY O_DIRECTORY
+#else
+#define NXT_FILE_DIRECTORY 0
+#endif
+
+#if defined(O_SEARCH)
+#define NXT_FILE_SEARCH (O_SEARCH|NXT_FILE_DIRECTORY)
+
+#elif defined(O_EXEC)
+#define NXT_FILE_SEARCH (O_EXEC|NXT_FILE_DIRECTORY)
+
+#else
+/*
+ * O_PATH is used in combination with O_RDONLY. The last one is ignored
+ * if O_PATH is used, but it allows Unit to not fail when it was built on
+ * modern system (i.e. glibc 2.14+) and run with a kernel older than 2.6.39.
+ * Then O_PATH is unknown to the kernel and ignored, while O_RDONLY is used.
+ */
+#define NXT_FILE_SEARCH (O_PATH|O_RDONLY|NXT_FILE_DIRECTORY)
+#endif
+
+#endif /* NXT_HAVE_OPENAT2 */
+
/* The file creation modes. */
#define NXT_FILE_CREATE_OR_OPEN O_CREAT
#define NXT_FILE_OPEN 0
diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c
index d3da6942..d3da6942 100644..100755
--- a/src/nxt_h1proto.c
+++ b/src/nxt_h1proto.c
diff --git a/src/nxt_http.h b/src/nxt_http.h
index e30bfeb4..f82d837e 100644
--- a/src/nxt_http.h
+++ b/src/nxt_http.h
@@ -197,7 +197,8 @@ struct nxt_http_request_s {
};
-typedef struct nxt_http_route_s nxt_http_route_t;
+typedef struct nxt_http_route_s nxt_http_route_t;
+typedef struct nxt_http_route_rule_s nxt_http_route_rule_t;
struct nxt_http_action_s {
@@ -206,16 +207,25 @@ struct nxt_http_action_s {
nxt_http_action_t *action);
union {
nxt_http_route_t *route;
- nxt_app_t *application;
- nxt_http_action_t *fallback;
nxt_upstream_t *upstream;
uint32_t upstream_number;
nxt_http_status_t return_code;
nxt_var_t *var;
+
+ struct {
+ nxt_app_t *application;
+ nxt_int_t target;
+ } app;
+
+ struct {
+ nxt_str_t chroot;
+ nxt_uint_t resolve;
+ nxt_http_route_rule_t *types;
+ nxt_http_action_t *fallback;
+ } share;
} u;
nxt_str_t name;
- nxt_int_t target;
};
@@ -298,6 +308,8 @@ nxt_int_t nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass,
nxt_str_t *segments, nxt_uint_t n);
nxt_http_action_t *nxt_http_pass_application(nxt_task_t *task,
nxt_router_conf_t *rtcf, nxt_str_t *name);
+nxt_int_t nxt_http_route_test_rule(nxt_http_request_t *r,
+ nxt_http_route_rule_t *rule, u_char *start, size_t length);
nxt_int_t nxt_upstreams_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_conf_value_t *conf);
diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c
index 650c1a89..779cfcf8 100644
--- a/src/nxt_http_request.c
+++ b/src/nxt_http_request.c
@@ -348,9 +348,9 @@ nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_str_set(&r->server_name, "localhost");
}
- r->app_target = action->target;
+ r->app_target = action->u.app.target;
- nxt_router_process_http_request(task, r, action->u.application);
+ nxt_router_process_http_request(task, r, action->u.app.application);
return NULL;
}
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index 28545fc9..15b85544 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -50,8 +50,12 @@ typedef struct {
nxt_conf_value_t *pass;
nxt_conf_value_t *ret;
nxt_str_t location;
- nxt_conf_value_t *share;
nxt_conf_value_t *proxy;
+ nxt_conf_value_t *share;
+ nxt_str_t chroot;
+ nxt_conf_value_t *follow_symlinks;
+ nxt_conf_value_t *traverse_mounts;
+ nxt_conf_value_t *types;
nxt_conf_value_t *fallback;
} nxt_http_route_action_conf_t;
@@ -112,7 +116,7 @@ typedef struct {
} nxt_http_cookie_t;
-typedef struct {
+struct nxt_http_route_rule_s {
/* The object must be the first field. */
nxt_http_route_object_t object:8;
uint32_t items;
@@ -128,7 +132,7 @@ typedef struct {
} u;
nxt_http_route_pattern_t pattern[0];
-} nxt_http_route_rule_t;
+};
typedef struct {
@@ -195,8 +199,9 @@ static nxt_http_route_t *nxt_http_route_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv);
static nxt_http_route_match_t *nxt_http_route_match_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv);
-static nxt_int_t nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf,
- nxt_conf_value_t *cv, nxt_http_action_t *action);
+static nxt_int_t nxt_http_route_action_create(nxt_task_t *task,
+ nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv,
+ nxt_http_action_t *action);
static nxt_http_route_table_t *nxt_http_route_table_create(nxt_task_t *task,
nxt_mp_t *mp, nxt_conf_value_t *table_cv, nxt_http_route_object_t object,
nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding);
@@ -274,8 +279,6 @@ static nxt_http_name_value_t *nxt_http_route_cookie(nxt_array_t *array,
u_char *name, size_t name_length, u_char *start, u_char *end);
static nxt_int_t nxt_http_route_test_cookie(nxt_http_request_t *r,
nxt_http_route_rule_t *rule, nxt_array_t *array);
-static nxt_int_t nxt_http_route_test_rule(nxt_http_request_t *r,
- nxt_http_route_rule_t *rule, u_char *start, size_t length);
static nxt_int_t nxt_http_route_pattern(nxt_http_request_t *r,
nxt_http_route_pattern_t *pattern, u_char *start, size_t length);
static nxt_int_t nxt_http_route_memcmp(u_char *start, u_char *test,
@@ -457,7 +460,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
match_conf = nxt_conf_get_path(cv, &match_path);
n = (match_conf != NULL) ? nxt_conf_object_members_count(match_conf) : 0;
- size = sizeof(nxt_http_route_match_t) + n * sizeof(nxt_http_route_rule_t *);
+ size = sizeof(nxt_http_route_match_t) + n * sizeof(nxt_http_route_test_t *);
mp = tmcf->router_conf->mem_pool;
@@ -473,7 +476,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
return NULL;
}
- ret = nxt_http_route_action_create(tmcf, action_conf, &match->action);
+ ret = nxt_http_route_action_create(task, tmcf, action_conf, &match->action);
if (nxt_slow_path(ret != NXT_OK)) {
return NULL;
}
@@ -627,14 +630,34 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = {
offsetof(nxt_http_route_action_conf_t, location)
},
{
+ nxt_string("proxy"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_http_route_action_conf_t, proxy)
+ },
+ {
nxt_string("share"),
NXT_CONF_MAP_PTR,
offsetof(nxt_http_route_action_conf_t, share)
},
{
- nxt_string("proxy"),
+ nxt_string("chroot"),
+ NXT_CONF_MAP_STR,
+ offsetof(nxt_http_route_action_conf_t, chroot)
+ },
+ {
+ nxt_string("follow_symlinks"),
NXT_CONF_MAP_PTR,
- offsetof(nxt_http_route_action_conf_t, proxy)
+ offsetof(nxt_http_route_action_conf_t, follow_symlinks)
+ },
+ {
+ nxt_string("traverse_mounts"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_http_route_action_conf_t, traverse_mounts)
+ },
+ {
+ nxt_string("types"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_http_route_action_conf_t, types)
},
{
nxt_string("fallback"),
@@ -645,14 +668,20 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = {
static nxt_int_t
-nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv,
- nxt_http_action_t *action)
+nxt_http_route_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
+ nxt_conf_value_t *cv, nxt_http_action_t *action)
{
+#if (NXT_HAVE_OPENAT2)
+ u_char *p;
+ uint8_t slash;
+ nxt_str_t *chroot;
+#endif
nxt_mp_t *mp;
nxt_int_t ret;
nxt_str_t name, *string;
nxt_uint_t encode;
nxt_conf_value_t *conf;
+ nxt_http_route_rule_t *rule;
nxt_http_route_action_conf_t accf;
nxt_memzero(&accf, sizeof(accf));
@@ -700,14 +729,14 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv,
return NXT_OK;
}
- conf = accf.pass;
-
if (accf.share != NULL) {
conf = accf.share;
- action->handler = nxt_http_static_handler;
} else if (accf.proxy != NULL) {
conf = accf.proxy;
+
+ } else {
+ conf = accf.pass;
}
nxt_conf_get_string(conf, &name);
@@ -717,14 +746,70 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv,
return NXT_ERROR;
}
- if (accf.fallback != NULL) {
- action->u.fallback = nxt_mp_alloc(mp, sizeof(nxt_http_action_t));
- if (nxt_slow_path(action->u.fallback == NULL)) {
- return NXT_ERROR;
+ if (accf.share != NULL) {
+ action->handler = nxt_http_static_handler;
+
+#if (NXT_HAVE_OPENAT2)
+ string = &accf.chroot;
+ chroot = &action->u.share.chroot;
+
+ if (string->length > 0) {
+ action->u.share.resolve |= RESOLVE_IN_ROOT;
+
+ slash = (string->start[string->length - 1] != '/');
+
+ chroot->length = string->length + (slash ? 1 : 0);
+
+ chroot->start = nxt_mp_alloc(mp, chroot->length + 1);
+ if (nxt_slow_path(chroot->start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ p = nxt_cpymem(chroot->start, string->start, string->length);
+
+ if (slash) {
+ *p++ = '/';
+ }
+
+ *p = '\0';
+ }
+
+ if (accf.follow_symlinks != NULL
+ && !nxt_conf_get_boolean(accf.follow_symlinks))
+ {
+ action->u.share.resolve |= RESOLVE_NO_SYMLINKS;
+ }
+
+ if (accf.traverse_mounts != NULL
+ && !nxt_conf_get_boolean(accf.traverse_mounts))
+ {
+ action->u.share.resolve |= RESOLVE_NO_XDEV;
+ }
+#endif
+
+ if (accf.types != NULL) {
+ rule = nxt_http_route_rule_create(task, mp, accf.types, 0,
+ NXT_HTTP_ROUTE_PATTERN_LOWCASE,
+ NXT_HTTP_ROUTE_ENCODING_NONE);
+ if (nxt_slow_path(rule == NULL)) {
+ return NXT_ERROR;
+ }
+
+ action->u.share.types = rule;
}
- return nxt_http_route_action_create(tmcf, accf.fallback,
- action->u.fallback);
+ if (accf.fallback != NULL) {
+ action->u.share.fallback = nxt_mp_alloc(mp,
+ sizeof(nxt_http_action_t));
+ if (nxt_slow_path(action->u.share.fallback == NULL)) {
+ return NXT_ERROR;
+ }
+
+ return nxt_http_route_action_create(task, tmcf, accf.fallback,
+ action->u.share.fallback);
+ }
+
+ return NXT_OK;
}
if (accf.proxy != NULL) {
@@ -1411,9 +1496,10 @@ nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
if (action->handler != NULL) {
if (action->handler == nxt_http_static_handler
- && action->u.fallback != NULL)
+ && action->u.share.fallback != NULL)
{
- return nxt_http_action_resolve(task, tmcf, action->u.fallback);
+ return nxt_http_action_resolve(task, tmcf,
+ action->u.share.fallback);
}
return NXT_OK;
@@ -1533,14 +1619,14 @@ nxt_http_pass_find(nxt_task_t *task, nxt_mp_t *mp, nxt_router_conf_t *rtcf,
}
if (segments[2].length != 0) {
- targets = action->u.application->targets;
+ targets = action->u.app.application->targets;
for (i = 0; !nxt_strstr_eq(&segments[2], &targets[i]); i++);
- action->target = i;
+ action->u.app.target = i;
} else {
- action->target = 0;
+ action->u.app.target = 0;
}
return NXT_OK;
@@ -1678,7 +1764,7 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf,
(void) nxt_router_listener_application(rtcf, name, action);
- action->target = 0;
+ action->u.app.target = 0;
return action;
}
@@ -2426,7 +2512,7 @@ nxt_http_route_test_cookie(nxt_http_request_t *r,
}
-static nxt_int_t
+nxt_int_t
nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule,
u_char *start, size_t length)
{
diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c
index df2655fc..c8b73fac 100644
--- a/src/nxt_http_static.c
+++ b/src/nxt_http_static.c
@@ -31,15 +31,15 @@ nxt_http_action_t *
nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_action_t *action)
{
- size_t alloc, encode;
- u_char *p;
+ size_t length, encode;
+ u_char *p, *fname;
struct tm tm;
nxt_buf_t *fb;
nxt_int_t ret;
- nxt_str_t index, extension, *mtype;
+ nxt_str_t index, extension, *mtype, *chroot;
nxt_uint_t level;
nxt_bool_t need_body;
- nxt_file_t *f;
+ nxt_file_t *f, file;
nxt_file_info_t fi;
nxt_http_field_t *field;
nxt_http_status_t status;
@@ -49,8 +49,8 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
if (nxt_slow_path(!nxt_str_eq(r->method, "GET", 3))) {
if (!nxt_str_eq(r->method, "HEAD", 4)) {
- if (action->u.fallback != NULL) {
- return action->u.fallback;
+ if (action->u.share.fallback != NULL) {
+ return action->u.share.fallback;
}
nxt_http_request_error(task, r, NXT_HTTP_METHOD_NOT_ALLOWED);
@@ -63,13 +63,6 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
need_body = 1;
}
- f = nxt_mp_zget(r->mem_pool, sizeof(nxt_file_t));
- if (nxt_slow_path(f == NULL)) {
- goto fail;
- }
-
- f->fd = NXT_FILE_INVALID;
-
if (r->path->start[r->path->length - 1] == '/') {
/* TODO: dynamic index setting. */
nxt_str_set(&index, "index.html");
@@ -80,23 +73,110 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_str_null(&extension);
}
- alloc = action->name.length + r->path->length + index.length + 1;
+ f = NULL;
+
+ rtcf = r->conf->socket_conf->router_conf;
+
+ mtype = NULL;
+
+ if (action->u.share.types != NULL && extension.start == NULL) {
+ nxt_http_static_extract_extension(r->path, &extension);
+ mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash,
+ &extension);
+
+ ret = nxt_http_route_test_rule(r, action->u.share.types,
+ mtype->start, mtype->length);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ goto fail;
+ }
+
+ if (ret == 0) {
+ if (action->u.share.fallback != NULL) {
+ return action->u.share.fallback;
+ }
- f->name = nxt_mp_nget(r->mem_pool, alloc);
- if (nxt_slow_path(f->name == NULL)) {
+ nxt_http_request_error(task, r, NXT_HTTP_FORBIDDEN);
+ return NULL;
+ }
+ }
+
+ length = action->name.length + r->path->length + index.length;
+
+ fname = nxt_mp_nget(r->mem_pool, length + 1);
+ if (nxt_slow_path(fname == NULL)) {
goto fail;
}
- p = f->name;
+ p = fname;
p = nxt_cpymem(p, action->name.start, action->name.length);
p = nxt_cpymem(p, r->path->start, r->path->length);
p = nxt_cpymem(p, index.start, index.length);
*p = '\0';
- ret = nxt_file_open(task, f, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
+ nxt_memzero(&file, sizeof(nxt_file_t));
+
+ file.name = fname;
+
+ chroot = &action->u.share.chroot;
+
+#if (NXT_HAVE_OPENAT2)
+
+ if (action->u.share.resolve != 0) {
+
+ if (chroot->length > 0) {
+ file.name = chroot->start;
+
+ if (length > chroot->length
+ && nxt_memcmp(fname, chroot->start, chroot->length) == 0)
+ {
+ fname += chroot->length;
+ ret = nxt_file_open(task, &file, NXT_FILE_SEARCH, NXT_FILE_OPEN,
+ 0);
+
+ } else {
+ file.error = NXT_EACCES;
+ ret = NXT_ERROR;
+ }
+
+ } else if (fname[0] == '/') {
+ file.name = (u_char *) "/";
+ ret = nxt_file_open(task, &file, NXT_FILE_SEARCH, NXT_FILE_OPEN, 0);
+
+ } else {
+ file.name = (u_char *) ".";
+ file.fd = AT_FDCWD;
+ ret = NXT_OK;
+ }
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ nxt_file_t af;
+
+ af = file;
+ nxt_memzero(&file, sizeof(nxt_file_t));
+ file.name = fname;
+
+ ret = nxt_file_openat2(task, &file, NXT_FILE_RDONLY,
+ NXT_FILE_OPEN, 0, af.fd,
+ action->u.share.resolve);
+
+ if (af.fd != AT_FDCWD) {
+ nxt_file_close(task, &af);
+ }
+ }
+
+ } else {
+ ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
+ }
+
+#else
+
+ ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
+
+#endif
if (nxt_slow_path(ret != NXT_OK)) {
- switch (f->error) {
+
+ switch (file.error) {
/*
* For Unix domain sockets "errno" is set to:
@@ -117,6 +197,10 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
break;
case NXT_EACCES:
+#if (NXT_HAVE_OPENAT2)
+ case NXT_ELOOP:
+ case NXT_EXDEV:
+#endif
level = NXT_LOG_ERR;
status = NXT_HTTP_FORBIDDEN;
break;
@@ -127,18 +211,32 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
break;
}
- if (level == NXT_LOG_ERR && action->u.fallback != NULL) {
- return action->u.fallback;
+ if (level == NXT_LOG_ERR && action->u.share.fallback != NULL) {
+ return action->u.share.fallback;
}
if (status != NXT_HTTP_NOT_FOUND) {
- nxt_log(task, level, "open(\"%FN\") failed %E", f->name, f->error);
+ if (chroot->length > 0) {
+ nxt_log(task, level, "opening \"%s\" at \"%V\" failed %E",
+ fname, chroot, file.error);
+
+ } else {
+ nxt_log(task, level, "opening \"%s\" failed %E",
+ fname, file.error);
+ }
}
nxt_http_request_error(task, r, status);
return NULL;
}
+ f = nxt_mp_get(r->mem_pool, sizeof(nxt_file_t));
+ if (nxt_slow_path(f == NULL)) {
+ goto fail;
+ }
+
+ *f = file;
+
ret = nxt_file_info(f, &fi);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
@@ -172,15 +270,15 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_field_name_set(field, "ETag");
- alloc = NXT_TIME_T_HEXLEN + NXT_OFF_T_HEXLEN + 3;
+ length = NXT_TIME_T_HEXLEN + NXT_OFF_T_HEXLEN + 3;
- p = nxt_mp_nget(r->mem_pool, alloc);
+ p = nxt_mp_nget(r->mem_pool, length);
if (nxt_slow_path(p == NULL)) {
goto fail;
}
field->value = p;
- field->value_length = nxt_sprintf(p, p + alloc, "\"%xT-%xO\"",
+ field->value_length = nxt_sprintf(p, p + length, "\"%xT-%xO\"",
nxt_file_mtime(&fi),
nxt_file_size(&fi))
- p;
@@ -189,12 +287,12 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_static_extract_extension(r->path, &extension);
}
- rtcf = r->conf->socket_conf->router_conf;
-
- mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash,
- &extension);
+ if (mtype == NULL) {
+ mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash,
+ &extension);
+ }
- if (mtype != NULL) {
+ if (mtype->length != 0) {
field = nxt_list_zero_add(r->resp.fields);
if (nxt_slow_path(field == NULL)) {
goto fail;
@@ -230,8 +328,8 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_file_close(task, f);
if (nxt_slow_path(!nxt_is_dir(&fi))) {
- if (action->u.fallback != NULL) {
- return action->u.fallback;
+ if (action->u.share.fallback != NULL) {
+ return action->u.share.fallback;
}
nxt_log(task, NXT_LOG_ERR, "\"%FN\" is not a regular file",
@@ -254,19 +352,19 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_field_name_set(field, "Location");
encode = nxt_encode_uri(NULL, r->path->start, r->path->length);
- alloc = r->path->length + encode * 2 + 1;
+ length = r->path->length + encode * 2 + 1;
if (r->args->length > 0) {
- alloc += 1 + r->args->length;
+ length += 1 + r->args->length;
}
- p = nxt_mp_nget(r->mem_pool, alloc);
+ p = nxt_mp_nget(r->mem_pool, length);
if (nxt_slow_path(p == NULL)) {
goto fail;
}
field->value = p;
- field->value_length = alloc;
+ field->value_length = length;
if (encode > 0) {
p = (u_char *) nxt_encode_uri(p, r->path->start, r->path->length);
@@ -294,7 +392,7 @@ fail:
nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
- if (f != NULL && f->fd != NXT_FILE_INVALID) {
+ if (f != NULL) {
nxt_file_close(task, f);
}
@@ -539,6 +637,8 @@ nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash)
{ nxt_string("application/octet-stream"), ".deb" },
{ nxt_string("application/octet-stream"), ".rpm" },
+
+ { nxt_string("application/x-httpd-php"), ".php" },
};
for (i = 0; i < nxt_nitems(default_types); i++) {
@@ -605,6 +705,8 @@ nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash, nxt_str_t *extension)
nxt_lvlhsh_query_t lhq;
nxt_http_static_mtype_t *mtype;
+ static nxt_str_t empty = nxt_string("");
+
lhq.key = *extension;
lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length);
lhq.proto = &nxt_http_static_mtypes_hash_proto;
@@ -614,7 +716,7 @@ nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash, nxt_str_t *extension)
return mtype->type;
}
- return NULL;
+ return &empty;
}
diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c
index f20f2c2c..00f336f6 100644
--- a/src/nxt_main_process.c
+++ b/src/nxt_main_process.c
@@ -211,6 +211,12 @@ static nxt_conf_map_t nxt_python_app_conf[] = {
},
{
+ nxt_string("targets"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_common_app_conf_t, u.python.targets),
+ },
+
+ {
nxt_string("thread_stack_size"),
NXT_CONF_MAP_INT32,
offsetof(nxt_common_app_conf_t, u.python.thread_stack_size),
diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c
index 9b86150f..2fd5d1bf 100644
--- a/src/nxt_openssl.c
+++ b/src/nxt_openssl.c
@@ -5,6 +5,7 @@
*/
#include <nxt_main.h>
+#include <nxt_conf.h>
#include <openssl/ssl.h>
#include <openssl/conf.h>
#include <openssl/err.h>
@@ -42,9 +43,14 @@ static unsigned long nxt_openssl_thread_id(void);
static void nxt_openssl_locks_free(void);
#endif
static nxt_int_t nxt_openssl_server_init(nxt_task_t *task,
- nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t last);
+ nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_conf_value_t *conf_cmds,
+ nxt_bool_t last);
static nxt_int_t nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx,
nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t single);
+#if (NXT_HAVE_OPENSSL_CONF_CMD)
+static nxt_int_t nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx,
+ nxt_conf_value_t *value, nxt_mp_t *mp);
+#endif
static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert,
nxt_tls_conf_t *conf, nxt_mp_t *mp);
static nxt_int_t nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq,
@@ -66,6 +72,8 @@ static void nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj,
void *data);
static nxt_int_t nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c,
int ret, nxt_err_t sys_err, nxt_openssl_io_t io);
+static void nxt_openssl_conn_io_shutdown_timeout(nxt_task_t *task, void *obj,
+ void *data);
static void nxt_cdecl nxt_openssl_conn_error(nxt_task_t *task,
nxt_err_t err, const char *fmt, ...);
static nxt_uint_t nxt_openssl_log_error_level(nxt_err_t err);
@@ -258,7 +266,7 @@ nxt_openssl_locks_free(void)
static nxt_int_t
nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
- nxt_mp_t *mp, nxt_bool_t last)
+ nxt_mp_t *mp, nxt_conf_value_t *conf_cmds, nxt_bool_t last)
{
SSL_CTX *ctx;
const char *ciphers, *ca_certificate;
@@ -318,6 +326,7 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
goto fail;
}
*/
+
ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5";
if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) {
@@ -327,6 +336,14 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
goto fail;
}
+#if (NXT_HAVE_OPENSSL_CONF_CMD)
+ if (conf_cmds != NULL
+ && nxt_ssl_conf_commands(task, ctx, conf_cmds, mp) != NXT_OK)
+ {
+ goto fail;
+ }
+#endif
+
SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
if (conf->ca_certificate != NULL) {
@@ -482,6 +499,89 @@ clean:
}
+#if (NXT_HAVE_OPENSSL_CONF_CMD)
+
+static nxt_int_t
+nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx, nxt_conf_value_t *conf,
+ nxt_mp_t *mp)
+{
+ int ret;
+ char *zcmd, *zvalue;
+ uint32_t index;
+ nxt_str_t cmd, value;
+ SSL_CONF_CTX *cctx;
+ nxt_conf_value_t *member;
+
+ cctx = SSL_CONF_CTX_new();
+ if (nxt_slow_path(cctx == NULL)) {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT,
+ "SSL_CONF_CTX_new() failed");
+ return NXT_ERROR;
+ }
+
+ SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE
+ | SSL_CONF_FLAG_SERVER
+ | SSL_CONF_FLAG_CERTIFICATE
+ | SSL_CONF_FLAG_SHOW_ERRORS);
+
+ SSL_CONF_CTX_set_ssl_ctx(cctx, ctx);
+
+ index = 0;
+
+ for ( ;; ) {
+ member = nxt_conf_next_object_member(conf, &cmd, &index);
+ if (nxt_slow_path(member == NULL)) {
+ break;
+ }
+
+ nxt_conf_get_string(member, &value);
+
+ zcmd = nxt_str_cstrz(mp, &cmd);
+ zvalue = nxt_str_cstrz(mp, &value);
+
+ if (nxt_slow_path(zcmd == NULL || zvalue == NULL)) {
+ goto fail;
+ }
+
+ ret = SSL_CONF_cmd(cctx, zcmd, zvalue);
+ if (ret == -2) {
+ nxt_openssl_log_error(task, NXT_LOG_ERR,
+ "unknown command \"%s\" in "
+ "\"conf_commands\" option", zcmd);
+ goto fail;
+ }
+
+ if (ret <= 0) {
+ nxt_openssl_log_error(task, NXT_LOG_ERR,
+ "invalid value \"%s\" for command \"%s\" "
+ "in \"conf_commands\" option",
+ zvalue, zcmd);
+ goto fail;
+ }
+
+ nxt_debug(task, "SSL_CONF_cmd(\"%s\", \"%s\")", zcmd, zvalue);
+ }
+
+ if (SSL_CONF_CTX_finish(cctx) != 1) {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT,
+ "SSL_CONF_finish() failed");
+ goto fail;
+ }
+
+ SSL_CONF_CTX_free(cctx);
+
+ return NXT_OK;
+
+fail:
+
+ SSL_CONF_CTX_free(cctx);
+
+ return NXT_ERROR;
+}
+
+#endif
+
+
static nxt_uint_t
nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf,
nxt_mp_t *mp)
@@ -548,7 +648,7 @@ nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf,
NULL, 0);
if (len <= 0) {
nxt_log(task, NXT_LOG_WARN, "certificate \"%V\" has neither "
- "Subject Alternative Name nor Common Name", bundle->name);
+ "Subject Alternative Name nor Common Name", &bundle->name);
return NXT_OK;
}
@@ -627,7 +727,7 @@ nxt_openssl_bundle_hash_insert(nxt_task_t *task, nxt_lvlhsh_t *lvlhsh,
if (item->name.length == 0 || item->name.start[0] != '.') {
nxt_log(task, NXT_LOG_WARN, "ignored invalid name \"%V\" "
"in certificate \"%V\": missing \".\" "
- "after wildcard symbol", &str, item->bundle->name);
+ "after wildcard symbol", &str, &item->bundle->name);
return NXT_OK;
}
}
@@ -642,7 +742,7 @@ nxt_openssl_bundle_hash_insert(nxt_task_t *task, nxt_lvlhsh_t *lvlhsh,
ret = nxt_lvlhsh_insert(lvlhsh, &lhq);
if (nxt_fast_path(ret == NXT_OK)) {
nxt_debug(task, "name \"%V\" for certificate \"%V\" is inserted",
- &str, item->bundle->name);
+ &str, &item->bundle->name);
return NXT_OK;
}
@@ -651,7 +751,7 @@ nxt_openssl_bundle_hash_insert(nxt_task_t *task, nxt_lvlhsh_t *lvlhsh,
if (old->bundle != item->bundle) {
nxt_log(task, NXT_LOG_WARN, "ignored duplicate name \"%V\" "
"in certificate \"%V\", identical name appears in \"%V\"",
- &str, old->bundle->name, item->bundle->name);
+ &str, &old->bundle->name, &item->bundle->name);
old->bundle = item->bundle;
}
@@ -728,8 +828,8 @@ nxt_openssl_servername(SSL *s, int *ad, void *arg)
if (bundle != NULL) {
nxt_debug(c->socket.task, "new tls context found for \"%V\": \"%V\" "
- "(old: \"%V\")", &str, bundle->name,
- conf->bundle->name);
+ "(old: \"%V\")", &str, &bundle->name,
+ &conf->bundle->name);
if (bundle != conf->bundle) {
if (SSL_set_SSL_CTX(s, bundle->ctx) == NULL) {
@@ -839,11 +939,7 @@ nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c)
c->sendfile = NXT_CONN_SENDFILE_OFF;
nxt_openssl_conn_handshake(task, c, c->socket.data);
- /*
- * TLS configuration might be destroyed after the TLS connection
- * is established.
- */
- tls->conf = NULL;
+
return;
fail:
@@ -1099,6 +1195,10 @@ nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, void *data)
SSL_set_quiet_shutdown(s, quiet);
+ if (tls->conf->no_wait_shutdown) {
+ mode |= SSL_RECEIVED_SHUTDOWN;
+ }
+
once = 1;
for ( ;; ) {
@@ -1153,7 +1253,8 @@ nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, void *data)
break;
case NXT_AGAIN:
- nxt_timer_add(task->thread->engine, &c->read_timer, 5000);
+ c->write_timer.handler = nxt_openssl_conn_io_shutdown_timeout;
+ nxt_timer_add(task->thread->engine, &c->write_timer, 5000);
return;
default:
@@ -1237,6 +1338,23 @@ nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c, int ret,
}
+static void
+nxt_openssl_conn_io_shutdown_timeout(nxt_task_t *task, void *obj, void *data)
+{
+ nxt_conn_t *c;
+ nxt_timer_t *timer;
+
+ timer = obj;
+
+ nxt_debug(task, "openssl conn shutdown timeout");
+
+ c = nxt_write_timer_conn(timer);
+
+ c->socket.timedout = 1;
+ nxt_openssl_conn_io_shutdown(task, c, NULL);
+}
+
+
static void nxt_cdecl
nxt_openssl_conn_error(nxt_task_t *task, nxt_err_t err, const char *fmt, ...)
{
diff --git a/src/nxt_pcre2.c b/src/nxt_pcre2.c
index 22c4d2d4..cb51062c 100644
--- a/src/nxt_pcre2.c
+++ b/src/nxt_pcre2.c
@@ -144,7 +144,7 @@ nxt_regex_match(nxt_regex_t *re, u_char *subject, size_t length,
if (pcre2_get_error_message(ret, errptr, ERR_BUF_SIZE) < 0) {
nxt_thread_log_error(NXT_LOG_ERR,
"pcre2_match() failed: %d on \"%*s\" "
- "using \"%V\"", ret, subject, length, subject,
+ "using \"%V\"", ret, length, subject,
&re->pattern);
} else {
diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c
index 8fbe7f65..3fb3b0db 100644
--- a/src/nxt_php_sapi.c
+++ b/src/nxt_php_sapi.c
@@ -163,7 +163,8 @@ static const zend_function_entry nxt_php_ext_functions[] = {
};
-zif_handler nxt_php_chdir_handler;
+zif_handler nxt_php_chdir_handler;
+zend_auto_global *nxt_php_server_ag;
static zend_module_entry nxt_php_unit_module = {
@@ -211,6 +212,7 @@ ZEND_NAMED_FUNCTION(nxt_php_chdir)
PHP_FUNCTION(fastcgi_finish_request)
{
+ zend_auto_global *ag;
nxt_php_run_ctx_t *ctx;
if (nxt_slow_path(zend_parse_parameters_none() == FAILURE)) {
@@ -240,6 +242,16 @@ PHP_FUNCTION(fastcgi_finish_request)
php_header(TSRMLS_C);
#endif
+ ag = nxt_php_server_ag;
+
+ if (ag->armed) {
+#ifdef NXT_PHP7
+ ag->armed = ag->auto_global_callback(ag->name);
+#else
+ ag->armed = ag->auto_global_callback(ag->name, ag->name_len TSRMLS_CC);
+#endif
+ }
+
nxt_unit_request_done(ctx->req, NXT_UNIT_OK);
ctx->req = NULL;
@@ -411,6 +423,19 @@ nxt_php_setup(nxt_task_t *task, nxt_process_t *process,
nxt_php_set_options(task, value, ZEND_INI_USER);
}
+#ifdef NXT_PHP7
+ nxt_php_server_ag = zend_hash_str_find_ptr(CG(auto_globals), "_SERVER",
+ nxt_length("_SERVER"));
+#else
+ zend_hash_quick_find(CG(auto_globals), "_SERVER", sizeof("_SERVER"),
+ zend_hash_func("_SERVER", sizeof("_SERVER")),
+ (void **) &nxt_php_server_ag);
+#endif
+ if (nxt_slow_path(nxt_php_server_ag == NULL)) {
+ nxt_alert(task, "failed to find $_SERVER auto global");
+ return NXT_ERROR;
+ }
+
return NXT_OK;
}
@@ -1076,10 +1101,20 @@ nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
nxt_memzero(&file_handle, sizeof(file_handle));
file_handle.type = ZEND_HANDLE_FILENAME;
+#if (PHP_VERSION_ID >= 80100)
+ file_handle.filename = zend_string_init((char *) ctx->script_filename.start,
+ ctx->script_filename.length, 0);
+ file_handle.primary_script = 1;
+#else
file_handle.filename = (char *) ctx->script_filename.start;
+#endif
php_execute_script(&file_handle TSRMLS_CC);
+#if (PHP_VERSION_ID >= 80100)
+ zend_destroy_file_handle(&file_handle);
+#endif
+
/* Prevention of consuming possible unread request body. */
#if (PHP_VERSION_ID < 50600)
read_post = sapi_module.read_post;
diff --git a/src/nxt_router.c b/src/nxt_router.c
index 8524b358..015ae226 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -41,8 +41,11 @@ typedef struct {
#if (NXT_TLS)
typedef struct {
- nxt_str_t name;
- nxt_socket_conf_t *conf;
+ nxt_str_t name;
+ nxt_socket_conf_t *socket_conf;
+ nxt_router_temp_conf_t *temp_conf;
+ nxt_conf_value_t *conf_cmds;
+ nxt_bool_t last;
nxt_queue_link_t link; /* for nxt_socket_conf_t.tls */
} nxt_router_tlssock_t;
@@ -117,12 +120,11 @@ static void nxt_router_listen_socket_ready(nxt_task_t *task,
static void nxt_router_listen_socket_error(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
#if (NXT_TLS)
-static void nxt_router_tls_rpc_create(nxt_task_t *task,
- nxt_router_temp_conf_t *tmcf, nxt_router_tlssock_t *tls, nxt_bool_t last);
static void nxt_router_tls_rpc_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
static nxt_int_t nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf,
- nxt_conf_value_t *value, nxt_socket_conf_t *skcf);
+ nxt_conf_value_t *value, nxt_socket_conf_t *skcf,
+ nxt_conf_value_t * conf_cmds);
#endif
static void nxt_router_app_rpc_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_app_t *app);
@@ -773,7 +775,7 @@ fail:
msg->port_msg.stream, 0, NULL);
if (tmcf != NULL) {
- nxt_mp_destroy(tmcf->mem_pool);
+ nxt_mp_release(tmcf->mem_pool);
}
cleanup:
@@ -954,8 +956,10 @@ nxt_router_conf_apply(nxt_task_t *task, void *obj, void *data)
tls = nxt_queue_link_data(qlk, nxt_router_tlssock_t, link);
- nxt_router_tls_rpc_create(task, tmcf, tls,
- nxt_queue_is_empty(&tmcf->tls));
+ tls->last = nxt_queue_is_empty(&tmcf->tls);
+
+ nxt_cert_store_get(task, &tls->name, tmcf->mem_pool,
+ nxt_router_tls_rpc_handler, tls);
return;
}
#endif
@@ -1061,7 +1065,7 @@ nxt_router_conf_ready(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
nxt_mp_destroy(rtcf->mem_pool);
}
- nxt_mp_destroy(tmcf->mem_pool);
+ nxt_mp_release(tmcf->mem_pool);
}
@@ -1120,7 +1124,7 @@ nxt_router_conf_error(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
nxt_router_conf_send(task, tmcf, NXT_PORT_MSG_RPC_ERROR);
- nxt_mp_destroy(tmcf->mem_pool);
+ nxt_mp_release(tmcf->mem_pool);
}
@@ -1337,7 +1341,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_router_t *router;
nxt_app_joint_t *app_joint;
#if (NXT_TLS)
- nxt_conf_value_t *certificate;
+ nxt_conf_value_t *certificate, *conf_cmds;
#endif
nxt_conf_value_t *conf, *http, *value, *websocket;
nxt_conf_value_t *applications, *application;
@@ -1358,6 +1362,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
static nxt_str_t access_log_path = nxt_string("/access_log");
#if (NXT_TLS)
static nxt_str_t certificate_path = nxt_string("/tls/certificate");
+ static nxt_str_t conf_commands_path = nxt_string("/tls/conf_commands");
#endif
static nxt_str_t static_path = nxt_string("/settings/http/static");
static nxt_str_t websocket_path = nxt_string("/settings/http/websocket");
@@ -1736,6 +1741,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
certificate = nxt_conf_get_path(listener, &certificate_path);
if (certificate != NULL) {
+ conf_cmds = nxt_conf_get_path(listener, &conf_commands_path);
+
if (nxt_conf_type(certificate) == NXT_CONF_ARRAY) {
n = nxt_conf_array_elements_count(certificate);
@@ -1744,7 +1751,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_assert(value != NULL);
- ret = nxt_router_conf_tls_insert(tmcf, value, skcf);
+ ret = nxt_router_conf_tls_insert(tmcf, value, skcf,
+ conf_cmds);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
@@ -1752,7 +1760,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
} else {
/* NXT_CONF_STRING */
- ret = nxt_router_conf_tls_insert(tmcf, certificate, skcf);
+ ret = nxt_router_conf_tls_insert(tmcf, certificate, skcf,
+ conf_cmds);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
@@ -1846,25 +1855,20 @@ fail:
static nxt_int_t
nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf,
- nxt_conf_value_t *value, nxt_socket_conf_t *skcf)
+ nxt_conf_value_t *value, nxt_socket_conf_t *skcf,
+ nxt_conf_value_t *conf_cmds)
{
- nxt_mp_t *mp;
- nxt_str_t str;
nxt_router_tlssock_t *tls;
- mp = tmcf->router_conf->mem_pool;
-
- tls = nxt_mp_get(mp, sizeof(nxt_router_tlssock_t));
+ tls = nxt_mp_get(tmcf->mem_pool, sizeof(nxt_router_tlssock_t));
if (nxt_slow_path(tls == NULL)) {
return NXT_ERROR;
}
- tls->conf = skcf;
- nxt_conf_get_string(value, &str);
-
- if (nxt_slow_path(nxt_str_dup(mp, &tls->name, &str) == NULL)) {
- return NXT_ERROR;
- }
+ tls->socket_conf = skcf;
+ tls->conf_cmds = conf_cmds;
+ tls->temp_conf = tmcf;
+ nxt_conf_get_string(value, &tls->name);
nxt_queue_insert_tail(&tmcf->tls, &tls->link);
@@ -2144,7 +2148,7 @@ nxt_router_listener_application(nxt_router_conf_t *rtcf, nxt_str_t *name,
return NXT_DECLINED;
}
- action->u.application = app;
+ action->u.app.application = app;
action->handler = nxt_http_application_handler;
return NXT_OK;
@@ -2428,42 +2432,20 @@ nxt_router_listen_socket_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
#if (NXT_TLS)
static void
-nxt_router_tls_rpc_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
- nxt_router_tlssock_t *tls, nxt_bool_t last)
-{
- nxt_socket_rpc_t *rpc;
-
- rpc = nxt_mp_alloc(tmcf->mem_pool, sizeof(nxt_socket_rpc_t));
- if (rpc == NULL) {
- nxt_router_conf_error(task, tmcf);
- return;
- }
-
- rpc->name = &tls->name;
- rpc->socket_conf = tls->conf;
- rpc->temp_conf = tmcf;
- rpc->last = last;
-
- nxt_cert_store_get(task, &tls->name, tmcf->mem_pool,
- nxt_router_tls_rpc_handler, rpc);
-}
-
-
-static void
nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
void *data)
{
nxt_mp_t *mp;
nxt_int_t ret;
nxt_tls_conf_t *tlscf;
- nxt_socket_rpc_t *rpc;
+ nxt_router_tlssock_t *tls;
nxt_tls_bundle_conf_t *bundle;
nxt_router_temp_conf_t *tmcf;
nxt_debug(task, "tls rpc handler");
- rpc = data;
- tmcf = rpc->temp_conf;
+ tls = data;
+ tmcf = tls->temp_conf;
if (msg == NULL || msg->port_msg.type == _NXT_PORT_MSG_RPC_ERROR) {
goto fail;
@@ -2471,16 +2453,17 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
mp = tmcf->router_conf->mem_pool;
- if (rpc->socket_conf->tls == NULL){
+ if (tls->socket_conf->tls == NULL){
tlscf = nxt_mp_zget(mp, sizeof(nxt_tls_conf_t));
if (nxt_slow_path(tlscf == NULL)) {
goto fail;
}
- rpc->socket_conf->tls = tlscf;
+ tlscf->no_wait_shutdown = 1;
+ tls->socket_conf->tls = tlscf;
} else {
- tlscf = rpc->socket_conf->tls;
+ tlscf = tls->socket_conf->tls;
}
bundle = nxt_mp_get(mp, sizeof(nxt_tls_bundle_conf_t));
@@ -2488,12 +2471,16 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
goto fail;
}
- bundle->name = rpc->name;
+ if (nxt_slow_path(nxt_str_dup(mp, &bundle->name, &tls->name) == NULL)) {
+ goto fail;
+ }
+
bundle->chain_file = msg->fd[0];
bundle->next = tlscf->bundle;
tlscf->bundle = bundle;
- ret = task->thread->runtime->tls->server_init(task, tlscf, mp, rpc->last);
+ ret = task->thread->runtime->tls->server_init(task, tlscf, mp,
+ tls->conf_cmds, tls->last);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
@@ -3223,12 +3210,11 @@ nxt_router_listen_socket_update(nxt_task_t *task, void *obj, void *data)
static void
nxt_router_listen_socket_delete(nxt_task_t *task, void *obj, void *data)
{
- nxt_joint_job_t *job;
- nxt_socket_conf_t *skcf;
- nxt_listen_event_t *lev;
- nxt_event_engine_t *engine;
+ nxt_socket_conf_t *skcf;
+ nxt_listen_event_t *lev;
+ nxt_event_engine_t *engine;
+ nxt_socket_conf_joint_t *joint;
- job = obj;
skcf = data;
engine = task->thread->engine;
@@ -3240,15 +3226,13 @@ nxt_router_listen_socket_delete(nxt_task_t *task, void *obj, void *data)
nxt_debug(task, "engine %p: listen socket delete: %d", engine,
lev->socket.fd);
+ joint = lev->socket.data;
+ joint->close_job = obj;
+
lev->timer.handler = nxt_router_listen_socket_close;
lev->timer.work_queue = &engine->fast_work_queue;
nxt_timer_add(engine, &lev->timer, 0);
-
- job->work.next = NULL;
- job->work.handler = nxt_router_conf_wait;
-
- nxt_event_engine_post(job->tmcf->engine, &job->work);
}
@@ -3273,6 +3257,7 @@ static void
nxt_router_listen_socket_close(nxt_task_t *task, void *obj, void *data)
{
nxt_timer_t *timer;
+ nxt_joint_job_t *job;
nxt_listen_event_t *lev;
nxt_socket_conf_joint_t *joint;
@@ -3292,6 +3277,12 @@ nxt_router_listen_socket_close(nxt_task_t *task, void *obj, void *data)
nxt_router_listen_socket_release(task, joint->socket_conf);
+ job = joint->close_job;
+ job->work.next = NULL;
+ job->work.handler = nxt_router_conf_wait;
+
+ nxt_event_engine_post(job->tmcf->engine, &job->work);
+
nxt_router_listen_event_release(task, lev, joint);
}
diff --git a/src/nxt_router.h b/src/nxt_router.h
index 5804840f..b1ccdf51 100644
--- a/src/nxt_router.h
+++ b/src/nxt_router.h
@@ -205,6 +205,8 @@ typedef struct {
nxt_event_engine_t *engine;
nxt_socket_conf_t *socket_conf;
+ nxt_joint_job_t *close_job;
+
nxt_upstream_t **upstreams;
/* Modules configuraitons. */
diff --git a/src/nxt_tls.h b/src/nxt_tls.h
index c44bfe56..63c49ee4 100644
--- a/src/nxt_tls.h
+++ b/src/nxt_tls.h
@@ -8,6 +8,9 @@
#define _NXT_TLS_H_INCLUDED_
+#include <nxt_conf.h>
+
+
/*
* The SSL/TLS libraries lack vector I/O interface yet add noticeable
* overhead to each SSL/TLS record so buffering allows to decrease the
@@ -32,6 +35,7 @@ typedef struct {
nxt_int_t (*server_init)(nxt_task_t *task,
nxt_tls_conf_t *conf, nxt_mp_t *mp,
+ nxt_conf_value_t *conf_cmds,
nxt_bool_t last);
void (*server_free)(nxt_task_t *task,
nxt_tls_conf_t *conf);
@@ -49,7 +53,7 @@ struct nxt_tls_bundle_conf_s {
void *ctx;
nxt_fd_t chain_file;
- nxt_str_t *name;
+ nxt_str_t name;
nxt_tls_bundle_conf_t *next;
};
@@ -69,6 +73,8 @@ struct nxt_tls_conf_s {
char *ca_certificate;
size_t buffer_size;
+
+ uint8_t no_wait_shutdown; /* 1 bit */
};
diff --git a/src/nxt_unix.h b/src/nxt_unix.h
index 609f7e95..393f61d9 100644
--- a/src/nxt_unix.h
+++ b/src/nxt_unix.h
@@ -242,6 +242,10 @@
#include <sys/mount.h>
#endif
+#if (NXT_HAVE_OPENAT2)
+#include <linux/openat2.h>
+#endif
+
#if (NXT_TEST_BUILD)
#include <nxt_test_build.h>
#endif
diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c
index d8204937..588a147a 100644
--- a/src/python/nxt_python.c
+++ b/src/python/nxt_python.c
@@ -24,6 +24,8 @@ typedef struct {
static nxt_int_t nxt_python_start(nxt_task_t *task,
nxt_process_data_t *data);
+static nxt_int_t nxt_python_set_target(nxt_task_t *task,
+ nxt_python_target_t *target, nxt_conf_value_t *conf);
static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value);
static int nxt_python_init_threads(nxt_python_app_conf_t *c);
static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx);
@@ -49,7 +51,7 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = {
};
static PyObject *nxt_py_stderr_flush;
-PyObject *nxt_py_application;
+nxt_python_targets_t *nxt_py_targets;
#if PY_MAJOR_VERSION == 3
static wchar_t *nxt_py_home;
@@ -66,18 +68,19 @@ static nxt_int_t
nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
{
int rc;
- char *nxt_py_module;
- size_t len;
+ size_t len, size;
+ uint32_t next;
PyObject *obj, *module;
- nxt_str_t proto;
- const char *callable;
+ nxt_str_t proto, probe_proto, name;
+ nxt_int_t ret, n, i;
nxt_unit_ctx_t *unit_ctx;
nxt_unit_init_t python_init;
+ nxt_conf_value_t *cv;
+ nxt_python_targets_t *targets;
nxt_common_app_conf_t *app_conf;
nxt_python_app_conf_t *c;
#if PY_MAJOR_VERSION == 3
char *path;
- size_t size;
nxt_int_t pep405;
static const char pyvenv[] = "/pyvenv.cfg";
@@ -190,38 +193,42 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
Py_CLEAR(obj);
- nxt_py_module = nxt_alloca(c->module.length + 1);
- nxt_memcpy(nxt_py_module, c->module.start, c->module.length);
- nxt_py_module[c->module.length] = '\0';
+ n = (c->targets != NULL ? nxt_conf_object_members_count(c->targets) : 1);
- module = PyImport_ImportModule(nxt_py_module);
- if (nxt_slow_path(module == NULL)) {
- nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module);
- nxt_python_print_exception();
+ size = sizeof(nxt_python_targets_t) + n * sizeof(nxt_python_target_t);
+
+ targets = nxt_unit_malloc(NULL, size);
+ if (nxt_slow_path(targets == NULL)) {
+ nxt_alert(task, "Could not allocate targets");
goto fail;
}
- callable = (c->callable != NULL) ? c->callable : "application";
+ memset(targets, 0, size);
- obj = PyDict_GetItemString(PyModule_GetDict(module), callable);
- if (nxt_slow_path(obj == NULL)) {
- nxt_alert(task, "Python failed to get \"%s\" "
- "from module \"%s\"", callable, nxt_py_module);
- goto fail;
- }
+ targets->count = n;
+ nxt_py_targets = targets;
- if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
- nxt_alert(task, "\"%s\" in module \"%s\" "
- "is not a callable object", callable, nxt_py_module);
- goto fail;
- }
+ if (c->targets != NULL) {
+ next = 0;
- nxt_py_application = obj;
- obj = NULL;
+ for (i = 0; /* void */; i++) {
+ cv = nxt_conf_next_object_member(c->targets, &name, &next);
+ if (cv == NULL) {
+ break;
+ }
- Py_INCREF(nxt_py_application);
+ ret = nxt_python_set_target(task, &targets->target[i], cv);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+ }
- Py_CLEAR(module);
+ } else {
+ ret = nxt_python_set_target(task, &targets->target[0], app_conf->self);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+ }
nxt_unit_default_init(task, &python_init);
@@ -232,7 +239,18 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
proto = c->protocol;
if (proto.length == 0) {
- proto = nxt_python_asgi_check(nxt_py_application) ? asgi : wsgi;
+ proto = nxt_python_asgi_check(targets->target[0].application)
+ ? asgi : wsgi;
+
+ for (i = 1; i < targets->count; i++) {
+ probe_proto = nxt_python_asgi_check(targets->target[i].application)
+ ? asgi : wsgi;
+ if (probe_proto.start != proto.start) {
+ nxt_alert(task, "A mix of ASGI & WSGI targets is forbidden, "
+ "specify protocol in config if incorrect");
+ goto fail;
+ }
+ }
}
if (nxt_strstr_eq(&proto, &asgi)) {
@@ -299,6 +317,81 @@ fail:
static nxt_int_t
+nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target,
+ nxt_conf_value_t *conf)
+{
+ char *callable, *module_name;
+ PyObject *module, *obj;
+ nxt_str_t str;
+ nxt_conf_value_t *value;
+
+ static nxt_str_t module_str = nxt_string("module");
+ static nxt_str_t callable_str = nxt_string("callable");
+
+ module = obj = NULL;
+
+ value = nxt_conf_get_object_member(conf, &module_str, NULL);
+ if (nxt_slow_path(value == NULL)) {
+ goto fail;
+ }
+
+ nxt_conf_get_string(value, &str);
+
+ module_name = nxt_alloca(str.length + 1);
+ nxt_memcpy(module_name, str.start, str.length);
+ module_name[str.length] = '\0';
+
+ module = PyImport_ImportModule(module_name);
+ if (nxt_slow_path(module == NULL)) {
+ nxt_alert(task, "Python failed to import module \"%s\"", module_name);
+ nxt_python_print_exception();
+ goto fail;
+ }
+
+ value = nxt_conf_get_object_member(conf, &callable_str, NULL);
+ if (value == NULL) {
+ callable = nxt_alloca(12);
+ nxt_memcpy(callable, "application", 12);
+
+ } else {
+ nxt_conf_get_string(value, &str);
+
+ callable = nxt_alloca(str.length + 1);
+ nxt_memcpy(callable, str.start, str.length);
+ callable[str.length] = '\0';
+ }
+
+ obj = PyDict_GetItemString(PyModule_GetDict(module), callable);
+ if (nxt_slow_path(obj == NULL)) {
+ nxt_alert(task, "Python failed to get \"%s\" from module \"%s\"",
+ callable, module);
+ goto fail;
+ }
+
+ if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
+ nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object",
+ callable, module);
+ goto fail;
+ }
+
+ target->application = obj;
+ obj = NULL;
+
+ Py_INCREF(target->application);
+ Py_CLEAR(module);
+
+ return NXT_OK;
+
+fail:
+
+ Py_XDECREF(obj);
+ Py_XDECREF(module);
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value)
{
int ret;
@@ -596,12 +689,21 @@ nxt_python_done_strings(nxt_python_string_t *pstr)
static void
nxt_python_atexit(void)
{
+ nxt_int_t i;
+
if (nxt_py_proto.done != NULL) {
nxt_py_proto.done();
}
Py_XDECREF(nxt_py_stderr_flush);
- Py_XDECREF(nxt_py_application);
+
+ if (nxt_py_targets != NULL) {
+ for (i = 0; i < nxt_py_targets->count; i++) {
+ Py_XDECREF(nxt_py_targets->target[i].application);
+ }
+
+ nxt_unit_free(NULL, nxt_py_targets);
+ }
Py_Finalize();
diff --git a/src/python/nxt_python.h b/src/python/nxt_python.h
index b581dd46..a5c1d9a6 100644
--- a/src/python/nxt_python.h
+++ b/src/python/nxt_python.h
@@ -37,13 +37,28 @@
#define NXT_HAVE_ASGI 1
#endif
-extern PyObject *nxt_py_application;
+
+typedef struct {
+ PyObject *application;
+ nxt_bool_t asgi_legacy;
+} nxt_python_target_t;
+
+
+typedef struct {
+ nxt_int_t count;
+ nxt_python_target_t target[0];
+} nxt_python_targets_t;
+
+
+extern nxt_python_targets_t *nxt_py_targets;
+
typedef struct {
nxt_str_t string;
PyObject **object_p;
} nxt_python_string_t;
+
typedef struct {
int (*ctx_data_alloc)(void **pdata);
void (*ctx_data_free)(void *data);
diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c
index a6f94507..1d220678 100644
--- a/src/python/nxt_python_asgi.c
+++ b/src/python/nxt_python_asgi.c
@@ -43,8 +43,6 @@ static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx);
static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args);
static void nxt_python_asgi_done(void);
-
-int nxt_py_asgi_legacy;
static PyObject *nxt_py_port_read;
static nxt_unit_port_t *nxt_py_shared_port;
@@ -137,6 +135,7 @@ int
nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
{
PyObject *func;
+ nxt_int_t i;
PyCodeObject *code;
nxt_unit_debug(NULL, "asgi_init");
@@ -161,21 +160,23 @@ nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
return NXT_UNIT_ERROR;
}
- func = nxt_python_asgi_get_func(nxt_py_application);
- if (nxt_slow_path(func == NULL)) {
- nxt_unit_alert(NULL, "Python cannot find function for callable");
- return NXT_UNIT_ERROR;
- }
+ for (i = 0; i < nxt_py_targets->count; i++) {
+ func = nxt_python_asgi_get_func(nxt_py_targets->target[i].application);
+ if (nxt_slow_path(func == NULL)) {
+ nxt_unit_alert(NULL, "Python cannot find function for callable");
+ return NXT_UNIT_ERROR;
+ }
- code = (PyCodeObject *) PyFunction_GET_CODE(func);
+ code = (PyCodeObject *) PyFunction_GET_CODE(func);
- if ((code->co_flags & CO_COROUTINE) == 0) {
- nxt_unit_debug(NULL, "asgi: callable is not a coroutine function "
- "switching to legacy mode");
- nxt_py_asgi_legacy = 1;
- }
+ if ((code->co_flags & CO_COROUTINE) == 0) {
+ nxt_unit_debug(NULL, "asgi: callable is not a coroutine function "
+ "switching to legacy mode");
+ nxt_py_targets->target[i].asgi_legacy = 1;
+ }
- Py_DECREF(func);
+ Py_DECREF(func);
+ }
init->callbacks.request_handler = nxt_py_asgi_request_handler;
init->callbacks.data_handler = nxt_py_asgi_http_data_handler;
@@ -408,6 +409,7 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
{
PyObject *scope, *res, *task, *receive, *send, *done, *asgi;
PyObject *stage2;
+ nxt_python_target_t *target;
nxt_py_asgi_ctx_data_t *ctx_data;
if (req->request->websocket_handshake) {
@@ -456,17 +458,18 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
}
req->data = asgi;
+ target = &nxt_py_targets->target[req->request->app_target];
- if (!nxt_py_asgi_legacy) {
+ if (!target->asgi_legacy) {
nxt_unit_req_debug(req, "Python call ASGI 3.0 application");
- res = PyObject_CallFunctionObjArgs(nxt_py_application,
+ res = PyObject_CallFunctionObjArgs(target->application,
scope, receive, send, NULL);
} else {
nxt_unit_req_debug(req, "Python call legacy application");
- res = PyObject_CallFunctionObjArgs(nxt_py_application, scope, NULL);
+ res = PyObject_CallFunctionObjArgs(target->application, scope, NULL);
if (nxt_slow_path(res == NULL)) {
nxt_unit_req_error(req, "Python failed to call legacy app stage1");
diff --git a/src/python/nxt_python_asgi.h b/src/python/nxt_python_asgi.h
index 37f2a099..20702065 100644
--- a/src/python/nxt_python_asgi.h
+++ b/src/python/nxt_python_asgi.h
@@ -33,7 +33,7 @@ typedef struct {
PyObject *loop_remove_reader;
PyObject *quit_future;
PyObject *quit_future_set_result;
- PyObject *lifespan;
+ PyObject **target_lifespans;
nxt_unit_port_t *port;
} nxt_py_asgi_ctx_data_t;
@@ -69,6 +69,4 @@ int nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data);
int nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx);
-extern int nxt_py_asgi_legacy;
-
#endif /* _NXT_PYTHON_ASGI_H_INCLUDED_ */
diff --git a/src/python/nxt_python_asgi_lifespan.c b/src/python/nxt_python_asgi_lifespan.c
index 506eaf4d..1fc0e6b7 100644
--- a/src/python/nxt_python_asgi_lifespan.c
+++ b/src/python/nxt_python_asgi_lifespan.c
@@ -27,7 +27,10 @@ typedef struct {
PyObject *receive_future;
} nxt_py_asgi_lifespan_t;
-
+static PyObject *nxt_py_asgi_lifespan_target_startup(
+ nxt_py_asgi_ctx_data_t *ctx_data, nxt_python_target_t *target);
+static int nxt_py_asgi_lifespan_target_shutdown(
+ nxt_py_asgi_lifespan_t *lifespan);
static PyObject *nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none);
static PyObject *nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict);
static PyObject *nxt_py_asgi_lifespan_send_startup(
@@ -69,24 +72,60 @@ static PyTypeObject nxt_py_asgi_lifespan_type = {
int
nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data)
{
- int rc;
+ size_t size;
+ PyObject *lifespan;
+ PyObject **target_lifespans;
+ nxt_int_t i;
+ nxt_python_target_t *target;
+
+ size = nxt_py_targets->count * sizeof(PyObject*);
+
+ target_lifespans = nxt_unit_malloc(NULL, size);
+ if (nxt_slow_path(target_lifespans == NULL)) {
+ nxt_unit_alert(NULL, "Failed to allocate lifespan data");
+ return NXT_UNIT_ERROR;
+ }
+
+ memset(target_lifespans, 0, size);
+
+ for (i = 0; i < nxt_py_targets->count; i++) {
+ target = &nxt_py_targets->target[i];
+
+ lifespan = nxt_py_asgi_lifespan_target_startup(ctx_data, target);
+ if (nxt_slow_path(lifespan == NULL)) {
+ return NXT_UNIT_ERROR;
+ }
+
+ target_lifespans[i] = lifespan;
+ }
+
+ ctx_data->target_lifespans = target_lifespans;
+
+ return NXT_UNIT_OK;
+}
+
+
+static PyObject *
+nxt_py_asgi_lifespan_target_startup(nxt_py_asgi_ctx_data_t *ctx_data,
+ nxt_python_target_t *target)
+{
PyObject *scope, *res, *py_task, *receive, *send, *done;
PyObject *stage2;
- nxt_py_asgi_lifespan_t *lifespan;
+ nxt_py_asgi_lifespan_t *lifespan, *ret;
if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_lifespan_type) != 0)) {
nxt_unit_alert(NULL,
"Python failed to initialize the 'asgi_lifespan' type object");
- return NXT_UNIT_ERROR;
+ return NULL;
}
lifespan = PyObject_New(nxt_py_asgi_lifespan_t, &nxt_py_asgi_lifespan_type);
if (nxt_slow_path(lifespan == NULL)) {
nxt_unit_alert(NULL, "Python failed to create lifespan object");
- return NXT_UNIT_ERROR;
+ return NULL;
}
- rc = NXT_UNIT_ERROR;
+ ret = NULL;
receive = PyObject_GetAttrString((PyObject *) lifespan, "receive");
if (nxt_slow_path(receive == NULL)) {
@@ -130,23 +169,25 @@ nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data)
goto release_future;
}
- if (!nxt_py_asgi_legacy) {
+ if (!target->asgi_legacy) {
nxt_unit_req_debug(NULL, "Python call ASGI 3.0 application");
- res = PyObject_CallFunctionObjArgs(nxt_py_application,
+ res = PyObject_CallFunctionObjArgs(target->application,
scope, receive, send, NULL);
} else {
nxt_unit_req_debug(NULL, "Python call legacy application");
- res = PyObject_CallFunctionObjArgs(nxt_py_application, scope, NULL);
+ res = PyObject_CallFunctionObjArgs(target->application, scope, NULL);
if (nxt_slow_path(res == NULL)) {
nxt_unit_log(NULL, NXT_UNIT_LOG_INFO,
"ASGI Lifespan processing exception");
nxt_python_print_exception();
lifespan->disabled = 1;
- rc = NXT_UNIT_OK;
+
+ Py_INCREF(lifespan);
+ ret = lifespan;
goto release_scope;
}
@@ -211,10 +252,9 @@ nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data)
Py_DECREF(res);
if (lifespan->startup_sent == 1 || lifespan->disabled) {
- ctx_data->lifespan = (PyObject *) lifespan;
- Py_INCREF(ctx_data->lifespan);
+ Py_INCREF(lifespan);
- rc = NXT_UNIT_OK;
+ ret = lifespan;
}
release_task:
@@ -232,20 +272,41 @@ release_receive:
release_lifespan:
Py_DECREF(lifespan);
- return rc;
+ return (PyObject *) ret;
}
int
nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx)
{
- PyObject *msg, *future, *res;
+ nxt_int_t i, ret;
nxt_py_asgi_lifespan_t *lifespan;
nxt_py_asgi_ctx_data_t *ctx_data;
ctx_data = ctx->data;
- lifespan = (nxt_py_asgi_lifespan_t *) ctx_data->lifespan;
+ for (i = 0; i < nxt_py_targets->count; i++) {
+ lifespan = (nxt_py_asgi_lifespan_t *)ctx_data->target_lifespans[i];
+
+ ret = nxt_py_asgi_lifespan_target_shutdown(lifespan);
+ if (nxt_slow_path(ret != NXT_UNIT_OK)) {
+ return NXT_UNIT_ERROR;
+ }
+ }
+
+ nxt_unit_free(NULL, ctx_data->target_lifespans);
+
+ return NXT_UNIT_OK;
+}
+
+
+static int
+nxt_py_asgi_lifespan_target_shutdown(nxt_py_asgi_lifespan_t *lifespan)
+{
+ PyObject *msg, *future, *res;
+ nxt_py_asgi_ctx_data_t *ctx_data;
+
+ ctx_data = lifespan->ctx_data;
if (nxt_slow_path(lifespan == NULL || lifespan->disabled)) {
return NXT_UNIT_OK;
diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c
index 77c45af5..b80d10fa 100644
--- a/src/python/nxt_python_wsgi.c
+++ b/src/python/nxt_python_wsgi.c
@@ -302,7 +302,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
{
int rc;
PyObject *environ, *args, *response, *iterator, *item;
- PyObject *close, *result;
+ PyObject *close, *result, *application;
nxt_bool_t prepare_environ;
nxt_python_ctx_t *pctx;
@@ -348,7 +348,8 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
Py_INCREF(pctx->start_resp);
PyTuple_SET_ITEM(args, 1, pctx->start_resp);
- response = PyObject_CallObject(nxt_py_application, args);
+ application = nxt_py_targets->target[req->request->app_target].application;
+ response = PyObject_CallObject(application, args);
Py_DECREF(args);
diff --git a/src/ruby/nxt_ruby_stream_io.c b/src/ruby/nxt_ruby_stream_io.c
index 69bf289e..82ad3908 100644
--- a/src/ruby/nxt_ruby_stream_io.c
+++ b/src/ruby/nxt_ruby_stream_io.c
@@ -25,7 +25,9 @@ nxt_ruby_stream_io_input_init(void)
{
VALUE stream_io;
- stream_io = rb_define_class("NGINX_Unit_Stream_IO_Read", rb_cData);
+ stream_io = rb_define_class("NGINX_Unit_Stream_IO_Read", rb_cObject);
+
+ rb_undef_alloc_func(stream_io);
rb_gc_register_address(&stream_io);
@@ -46,7 +48,9 @@ nxt_ruby_stream_io_error_init(void)
{
VALUE stream_io;
- stream_io = rb_define_class("NGINX_Unit_Stream_IO_Error", rb_cData);
+ stream_io = rb_define_class("NGINX_Unit_Stream_IO_Error", rb_cObject);
+
+ rb_undef_alloc_func(stream_io);
rb_gc_register_address(&stream_io);