summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/java/nginx/unit/Context.java68
-rw-r--r--src/java/nginx/unit/Response.java21
-rw-r--r--src/nodejs/unit-http/unit.cpp2
-rw-r--r--src/nxt_application.c19
-rw-r--r--src/nxt_application.h8
-rw-r--r--src/nxt_conf_validation.c1341
-rw-r--r--src/nxt_external.c8
-rw-r--r--src/nxt_fs.c125
-rw-r--r--src/nxt_fs.h65
-rw-r--r--src/nxt_h1proto.c6
-rw-r--r--src/nxt_http.h6
-rw-r--r--src/nxt_http_parse.c67
-rw-r--r--src/nxt_http_parse.h14
-rw-r--r--src/nxt_http_request.c2
-rw-r--r--src/nxt_http_route.c99
-rw-r--r--src/nxt_isolation.c145
-rw-r--r--src/nxt_java.c199
-rw-r--r--src/nxt_lib.c5
-rw-r--r--src/nxt_main_process.c55
-rw-r--r--src/nxt_pcre.c135
-rw-r--r--src/nxt_pcre2.c161
-rw-r--r--src/nxt_php_sapi.c93
-rw-r--r--src/nxt_port.h3
-rw-r--r--src/nxt_port_memory.c46
-rw-r--r--src/nxt_port_memory.h2
-rw-r--r--src/nxt_process.c5
-rw-r--r--src/nxt_process.h4
-rw-r--r--src/nxt_regex.h41
-rw-r--r--src/nxt_router.c61
-rw-r--r--src/nxt_router.h2
-rw-r--r--src/nxt_runtime.c4
-rw-r--r--src/nxt_unit.c471
-rw-r--r--src/nxt_unit.h9
-rw-r--r--src/perl/nxt_perl_psgi.c494
-rw-r--r--src/perl/nxt_perl_psgi_layer.h6
-rw-r--r--src/python/nxt_python.c276
-rw-r--r--src/python/nxt_python.h24
-rw-r--r--src/python/nxt_python_asgi.c733
-rw-r--r--src/python/nxt_python_asgi.h34
-rw-r--r--src/python/nxt_python_asgi_http.c113
-rw-r--r--src/python/nxt_python_asgi_lifespan.c143
-rw-r--r--src/python/nxt_python_asgi_str.c2
-rw-r--r--src/python/nxt_python_asgi_str.h2
-rw-r--r--src/python/nxt_python_asgi_websocket.c21
-rw-r--r--src/python/nxt_python_wsgi.c448
-rw-r--r--src/ruby/nxt_ruby.c809
-rw-r--r--src/ruby/nxt_ruby.h8
-rw-r--r--src/ruby/nxt_ruby_stream_io.c51
-rw-r--r--src/test/nxt_http_parse_test.c38
-rw-r--r--src/test/nxt_unit_app_test.c134
-rw-r--r--src/test/nxt_unit_websocket_chat.c8
51 files changed, 4696 insertions, 1940 deletions
diff --git a/src/java/nginx/unit/Context.java b/src/java/nginx/unit/Context.java
index 5f7ec22f..0197858b 100644
--- a/src/java/nginx/unit/Context.java
+++ b/src/java/nginx/unit/Context.java
@@ -443,7 +443,7 @@ public class Context implements ServletContext, InitParams
.enableClassInfo()
.enableAnnotationInfo()
//.enableSystemPackages()
- .whitelistModules("javax.*")
+ .acceptModules("javax.*")
//.enableAllInfo()
;
@@ -1214,6 +1214,16 @@ public class Context implements ServletContext, InitParams
processXmlInitParam(reg, (Element) child_node);
continue;
}
+
+ if (tag_name.equals("filter-name")
+ || tag_name.equals("#text")
+ || tag_name.equals("#comment"))
+ {
+ continue;
+ }
+
+ log("processWebXml: tag '" + tag_name + "' for filter '"
+ + filter_name + "' is ignored");
}
filters_.add(reg);
@@ -1306,6 +1316,22 @@ public class Context implements ServletContext, InitParams
reg.setLoadOnStartup(Integer.parseInt(child_node.getTextContent().trim()));
continue;
}
+
+ if (tag_name.equals("jsp-file")) {
+ reg.setJspFile(child_node.getTextContent().trim());
+ continue;
+ }
+
+ if (tag_name.equals("servlet-name")
+ || tag_name.equals("display-name")
+ || tag_name.equals("#text")
+ || tag_name.equals("#comment"))
+ {
+ continue;
+ }
+
+ log("processWebXml: tag '" + tag_name + "' for servlet '"
+ + servlet_name + "' is ignored");
}
servlets_.add(reg);
@@ -1888,6 +1914,7 @@ public class Context implements ServletContext, InitParams
private boolean initialized_ = false;
private final List<FilterMap> filters_ = new ArrayList<>();
private boolean system_jsp_servlet_ = false;
+ private String jsp_file_;
private MultipartConfigElement multipart_config_;
public ServletReg(String name, Class<?> servlet_class)
@@ -1921,6 +1948,21 @@ public class Context implements ServletContext, InitParams
trace("ServletReg.init(): " + getName());
+ if (jsp_file_ != null) {
+ setInitParameter("jspFile", jsp_file_);
+ jsp_file_ = null;
+
+ ServletReg jsp_servlet = name2servlet_.get("jsp");
+
+ if (jsp_servlet.servlet_class_ != null) {
+ servlet_class_ = jsp_servlet.servlet_class_;
+ } else {
+ setClassName(jsp_servlet.getClassName());
+ }
+
+ system_jsp_servlet_ = jsp_servlet.system_jsp_servlet_;
+ }
+
if (system_jsp_servlet_) {
JasperInitializer ji = new JasperInitializer();
@@ -1972,6 +2014,10 @@ public class Context implements ServletContext, InitParams
throw new IllegalStateException("Class already initialized");
}
+ if (jsp_file_ != null) {
+ throw new IllegalStateException("jsp-file already initialized");
+ }
+
super.setClassName(class_name);
}
@@ -1985,11 +2031,31 @@ public class Context implements ServletContext, InitParams
throw new IllegalStateException("Class already initialized");
}
+ if (jsp_file_ != null) {
+ throw new IllegalStateException("jsp-file already initialized");
+ }
+
super.setClassName(servlet_class.getName());
servlet_class_ = servlet_class;
getAnnotationMultipartConfig();
}
+ public void setJspFile(String jsp_file) throws IllegalStateException
+ {
+ if (servlet_ != null
+ || servlet_class_ != null
+ || getClassName() != null)
+ {
+ throw new IllegalStateException("Class already initialized");
+ }
+
+ if (jsp_file_ != null) {
+ throw new IllegalStateException("jsp-file already initialized");
+ }
+
+ jsp_file_ = jsp_file;
+ }
+
private void getAnnotationMultipartConfig() {
if (servlet_class_ == null) {
return;
diff --git a/src/java/nginx/unit/Response.java b/src/java/nginx/unit/Response.java
index 099d7f15..268b359d 100644
--- a/src/java/nginx/unit/Response.java
+++ b/src/java/nginx/unit/Response.java
@@ -40,11 +40,13 @@ public class Response implements HttpServletResponse {
private String characterEncoding = defaultCharacterEncoding;
private String contentType = null;
private String contentTypeHeader = null;
+ private Locale locale = null;
private static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
private static final Charset UTF_8 = StandardCharsets.UTF_8;
private static final String CONTENT_TYPE = "Content-Type";
+ private static final byte[] CONTENT_LANGUAGE_BYTES = "Content-Language".getBytes(ISO_8859_1);
private static final byte[] SET_COOKIE_BYTES = "Set-Cookie".getBytes(ISO_8859_1);
private static final byte[] EXPIRES_BYTES = "Expires".getBytes(ISO_8859_1);
@@ -590,9 +592,13 @@ public class Response implements HttpServletResponse {
@Override
public Locale getLocale()
{
- log("getLocale");
+ trace("getLocale");
- return null;
+ if (locale == null) {
+ return Locale.getDefault();
+ }
+
+ return locale;
}
@Override
@@ -795,7 +801,16 @@ public class Response implements HttpServletResponse {
@Override
public void setLocale(Locale loc)
{
- log("setLocale: " + loc);
+ trace("setLocale: " + loc);
+
+ if (loc == null || isCommitted()) {
+ return;
+ }
+
+ locale = loc;
+ String lang = locale.toString().replace('_', '-');
+
+ setHeader(req_info_ptr, CONTENT_LANGUAGE_BYTES, lang.getBytes(ISO_8859_1));
}
private void log(String msg)
diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp
index 1ee5b742..c5bca49a 100644
--- a/src/nodejs/unit-http/unit.cpp
+++ b/src/nodejs/unit-http/unit.cpp
@@ -307,6 +307,8 @@ Unit::close_handler(nxt_unit_request_info_t *req)
} catch (exception &e) {
nxt_unit_req_warn(req, "close_handler: %s", e.str);
+ nxt_unit_request_done(req, NXT_UNIT_ERROR);
+
return;
}
diff --git a/src/nxt_application.c b/src/nxt_application.c
index 6935346c..5d58e60c 100644
--- a/src/nxt_application.c
+++ b/src/nxt_application.c
@@ -208,14 +208,14 @@ nxt_discovery_modules(nxt_task_t *task, const char *path)
mounts = module[i].mounts;
size += mounts->nelts * nxt_length("{\"src\": \"\", \"dst\": \"\", "
- "\"fstype\": \"\", \"flags\": , "
- "\"data\": \"\"},");
+ "\"type\": , \"name\": \"\", "
+ "\"flags\": , \"data\": \"\"},");
mnt = mounts->elts;
for (j = 0; j < mounts->nelts; j++) {
size += nxt_strlen(mnt[j].src) + nxt_strlen(mnt[j].dst)
- + nxt_strlen(mnt[j].fstype) + NXT_INT_T_LEN
+ + nxt_strlen(mnt[j].name) + (2 * NXT_INT_T_LEN)
+ (mnt[j].data == NULL ? 0 : nxt_strlen(mnt[j].data));
}
}
@@ -242,9 +242,10 @@ nxt_discovery_modules(nxt_task_t *task, const char *path)
for (j = 0; j < mounts->nelts; j++) {
p = nxt_sprintf(p, end,
"{\"src\": \"%s\", \"dst\": \"%s\", "
- "\"fstype\": \"%s\", \"flags\": %d, "
+ "\"name\": \"%s\", \"type\": %d, \"flags\": %d, "
"\"data\": \"%s\"},",
- mnt[j].src, mnt[j].dst, mnt[j].fstype, mnt[j].flags,
+ mnt[j].src, mnt[j].dst, mnt[j].name, mnt[j].type,
+ mnt[j].flags,
mnt[j].data == NULL ? (u_char *) "" : mnt[j].data);
}
@@ -386,11 +387,13 @@ nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules,
goto fail;
}
- to->fstype = nxt_cstr_dup(mp, to->fstype, from->fstype);
- if (nxt_slow_path(to->fstype == NULL)) {
+ to->name = nxt_cstr_dup(mp, to->name, from->name);
+ if (nxt_slow_path(to->name == NULL)) {
goto fail;
}
+ to->type = from->type;
+
if (from->data != NULL) {
to->data = nxt_cstr_dup(mp, to->data, from->data);
if (nxt_slow_path(to->data == NULL)) {
@@ -723,7 +726,7 @@ nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init)
init->read_port.id.pid = my_port->pid;
init->read_port.id.id = my_port->id;
init->read_port.in_fd = my_port->pair[0];
- init->read_port.out_fd = -1;
+ init->read_port.out_fd = my_port->pair[1];
init->log_fd = 2;
diff --git a/src/nxt_application.h b/src/nxt_application.h
index cb49a033..5632f56f 100644
--- a/src/nxt_application.h
+++ b/src/nxt_application.h
@@ -51,6 +51,9 @@ typedef struct {
nxt_str_t path;
nxt_str_t module;
char *callable;
+ nxt_str_t protocol;
+ uint32_t threads;
+ uint32_t thread_stack_size;
} nxt_python_app_conf_t;
@@ -62,11 +65,14 @@ typedef struct {
typedef struct {
char *script;
+ uint32_t threads;
+ uint32_t thread_stack_size;
} nxt_perl_app_conf_t;
typedef struct {
nxt_str_t script;
+ uint32_t threads;
} nxt_ruby_app_conf_t;
@@ -75,6 +81,8 @@ typedef struct {
char *webapp;
nxt_conf_value_t *options;
char *unit_jars;
+ uint32_t threads;
+ uint32_t thread_stack_size;
} nxt_java_app_conf_t;
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index 4364057b..acb2e3de 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -11,6 +11,7 @@
#include <nxt_http.h>
#include <nxt_sockaddr.h>
#include <nxt_http_route_addr.h>
+#include <nxt_regex.h>
typedef enum {
@@ -39,26 +40,34 @@ typedef enum {
typedef nxt_int_t (*nxt_conf_vldt_handler_t)(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value,
void *data);
+typedef nxt_int_t (*nxt_conf_vldt_member_t)(nxt_conf_validation_t *vldt,
+ nxt_str_t *name,
+ nxt_conf_value_t *value);
+typedef nxt_int_t (*nxt_conf_vldt_element_t)(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value);
-typedef struct {
- nxt_str_t name;
- nxt_conf_vldt_type_t type:32;
- nxt_conf_vldt_flags_t flags:32;
- nxt_conf_vldt_handler_t validator;
- void *data;
-} nxt_conf_vldt_object_t;
+typedef struct nxt_conf_vldt_object_s nxt_conf_vldt_object_t;
+struct nxt_conf_vldt_object_s {
+ nxt_str_t name;
+ nxt_conf_vldt_type_t type:32;
+ nxt_conf_vldt_flags_t flags:32;
+ nxt_conf_vldt_handler_t validator;
-#define NXT_CONF_VLDT_NEXT(f) { nxt_null_string, 0, 0, NULL, (f) }
-#define NXT_CONF_VLDT_END { nxt_null_string, 0, 0, NULL, NULL }
+ union {
+ nxt_conf_vldt_object_t *members;
+ nxt_conf_vldt_object_t *next;
+ nxt_conf_vldt_member_t object;
+ nxt_conf_vldt_element_t array;
+ const char *string;
+ } u;
+};
-typedef nxt_int_t (*nxt_conf_vldt_member_t)(nxt_conf_validation_t *vldt,
- nxt_str_t *name,
- nxt_conf_value_t *value);
-typedef nxt_int_t (*nxt_conf_vldt_element_t)(nxt_conf_validation_t *vldt,
- nxt_conf_value_t *value);
+#define NXT_CONF_VLDT_NEXT(next) { .u.members = next }
+#define NXT_CONF_VLDT_END { .name = nxt_null_string }
+
static nxt_int_t nxt_conf_vldt_type(nxt_conf_validation_t *vldt,
nxt_str_t *name, nxt_conf_value_t *value, nxt_conf_vldt_type_t type);
@@ -87,6 +96,12 @@ 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_protocol(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_threads(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_thread_stack_size(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_routes(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_routes_member(nxt_conf_validation_t *vldt,
@@ -170,796 +185,718 @@ static nxt_int_t nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value);
#endif
-static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[] = {
- { nxt_string("read_timeout"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("keepalive_interval"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("max_frame_size"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
+
+static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[];
+static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[];
+static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[];
+static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[];
+#if (NXT_TLS)
+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_php_common_members[];
+static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_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[];
+static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[];
+static nxt_conf_vldt_object_t nxt_conf_vldt_app_namespaces_members[];
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[];
+#endif
+
+
+static nxt_conf_vldt_object_t nxt_conf_vldt_root_members[] = {
+ {
+ .name = nxt_string("settings"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_setting_members,
+ }, {
+ .name = nxt_string("listeners"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object_iterator,
+ .u.object = nxt_conf_vldt_listener,
+ }, {
+ .name = nxt_string("routes"),
+ .type = NXT_CONF_VLDT_ARRAY | NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_routes,
+ }, {
+ .name = nxt_string("applications"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object_iterator,
+ .u.object = nxt_conf_vldt_app,
+ }, {
+ .name = nxt_string("upstreams"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object_iterator,
+ .u.object = nxt_conf_vldt_upstream,
+ }, {
+ .name = nxt_string("access_log"),
+ .type = NXT_CONF_VLDT_STRING,
+ },
NXT_CONF_VLDT_END
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[] = {
- { nxt_string("mime_types"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_mtypes,
- NULL },
+static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[] = {
+ {
+ .name = nxt_string("http"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_http_members,
+ },
NXT_CONF_VLDT_END
};
static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[] = {
- { nxt_string("header_read_timeout"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("body_read_timeout"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("send_timeout"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("idle_timeout"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("body_buffer_size"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("max_body_size"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("body_temp_path"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- { nxt_string("websocket"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object,
- (void *) &nxt_conf_vldt_websocket_members },
-
- { nxt_string("static"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object,
- (void *) &nxt_conf_vldt_static_members },
+ {
+ .name = nxt_string("header_read_timeout"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("body_read_timeout"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("send_timeout"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("idle_timeout"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("body_buffer_size"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("max_body_size"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("body_temp_path"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("discard_unsafe_fields"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ }, {
+ .name = nxt_string("websocket"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_websocket_members,
+ }, {
+ .name = nxt_string("static"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_static_members,
+ },
NXT_CONF_VLDT_END
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[] = {
- { nxt_string("http"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object,
- (void *) &nxt_conf_vldt_http_members },
+static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[] = {
+ {
+ .name = nxt_string("read_timeout"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+
+ .name = nxt_string("keepalive_interval"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("max_frame_size"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ },
NXT_CONF_VLDT_END
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_root_members[] = {
- { nxt_string("settings"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object,
- (void *) &nxt_conf_vldt_setting_members },
-
- { nxt_string("listeners"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object_iterator,
- (void *) &nxt_conf_vldt_listener },
-
- { nxt_string("routes"),
- NXT_CONF_VLDT_ARRAY | NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_routes,
- NULL },
-
- { nxt_string("applications"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object_iterator,
- (void *) &nxt_conf_vldt_app },
-
- { nxt_string("upstreams"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object_iterator,
- (void *) &nxt_conf_vldt_upstream },
-
- { nxt_string("access_log"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
+static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[] = {
+ {
+ .name = nxt_string("mime_types"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_mtypes,
+ },
NXT_CONF_VLDT_END
};
-#if (NXT_TLS)
+static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = {
+ {
+ .name = nxt_string("pass"),
+ .type = NXT_CONF_VLDT_STRING,
+ .validator = nxt_conf_vldt_pass,
+ }, {
+ .name = nxt_string("application"),
+ .type = NXT_CONF_VLDT_STRING,
+ .validator = nxt_conf_vldt_app_name,
+ },
-static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = {
- { nxt_string("certificate"),
- NXT_CONF_VLDT_STRING,
- 0,
- &nxt_conf_vldt_certificate,
- NULL },
+#if (NXT_TLS)
+ {
+ .name = nxt_string("tls"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_tls_members,
+ },
+#endif
NXT_CONF_VLDT_END
};
-#endif
-
-
-static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = {
- { nxt_string("pass"),
- NXT_CONF_VLDT_STRING,
- 0,
- &nxt_conf_vldt_pass,
- NULL },
-
- { nxt_string("application"),
- NXT_CONF_VLDT_STRING,
- 0,
- &nxt_conf_vldt_app_name,
- NULL },
#if (NXT_TLS)
- { nxt_string("tls"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object,
- (void *) &nxt_conf_vldt_tls_members },
+static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = {
+ {
+ .name = nxt_string("certificate"),
+ .type = NXT_CONF_VLDT_STRING,
+ .validator = nxt_conf_vldt_certificate,
+ },
+
+ NXT_CONF_VLDT_END
+};
#endif
+
+static nxt_conf_vldt_object_t nxt_conf_vldt_route_members[] = {
+ {
+ .name = nxt_string("match"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_match_members,
+ }, {
+ .name = nxt_string("action"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_action,
+ },
+
NXT_CONF_VLDT_END
};
static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = {
- { nxt_string("method"),
- NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
- 0,
- &nxt_conf_vldt_match_patterns,
- NULL },
-
- { nxt_string("scheme"),
- NXT_CONF_VLDT_STRING,
- 0,
- &nxt_conf_vldt_match_scheme_pattern,
- NULL },
-
- { nxt_string("host"),
- NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
- 0,
- &nxt_conf_vldt_match_patterns,
- NULL },
-
- { nxt_string("source"),
- NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
- 0,
- &nxt_conf_vldt_match_addrs,
- NULL },
-
- { nxt_string("destination"),
- NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
- 0,
- &nxt_conf_vldt_match_addrs,
- NULL },
-
- { nxt_string("uri"),
- NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
- 0,
- &nxt_conf_vldt_match_encoded_patterns,
- NULL },
-
- { nxt_string("arguments"),
- NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
- 0,
- &nxt_conf_vldt_match_encoded_patterns_sets,
- NULL },
-
- { nxt_string("headers"),
- NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
- 0,
- &nxt_conf_vldt_match_patterns_sets,
- NULL },
-
- { nxt_string("cookies"),
- NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
- 0,
- &nxt_conf_vldt_match_patterns_sets,
- NULL },
+ {
+ .name = nxt_string("method"),
+ .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_match_patterns,
+ }, {
+ .name = nxt_string("scheme"),
+ .type = NXT_CONF_VLDT_STRING,
+ .validator = nxt_conf_vldt_match_scheme_pattern,
+ }, {
+ .name = nxt_string("host"),
+ .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_match_patterns,
+ }, {
+ .name = nxt_string("source"),
+ .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_match_addrs,
+ }, {
+ .name = nxt_string("destination"),
+ .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_match_addrs,
+ }, {
+ .name = nxt_string("uri"),
+ .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_match_encoded_patterns,
+ }, {
+ .name = nxt_string("arguments"),
+ .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_match_encoded_patterns_sets,
+ }, {
+ .name = nxt_string("headers"),
+ .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_match_patterns_sets,
+ }, {
+ .name = nxt_string("cookies"),
+ .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_match_patterns_sets,
+ },
NXT_CONF_VLDT_END
};
static nxt_conf_vldt_object_t nxt_conf_vldt_pass_action_members[] = {
- { nxt_string("pass"),
- NXT_CONF_VLDT_STRING,
- 0,
- &nxt_conf_vldt_pass,
- NULL },
+ {
+ .name = nxt_string("pass"),
+ .type = NXT_CONF_VLDT_STRING,
+ .validator = nxt_conf_vldt_pass,
+ },
NXT_CONF_VLDT_END
};
static nxt_conf_vldt_object_t nxt_conf_vldt_return_action_members[] = {
- { nxt_string("return"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- &nxt_conf_vldt_return,
- NULL },
-
- { nxt_string("location"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
+ {
+ .name = nxt_string("return"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ .validator = nxt_conf_vldt_return,
+ }, {
+ .name = nxt_string("location"),
+ .type = NXT_CONF_VLDT_STRING,
+ },
NXT_CONF_VLDT_END
};
static nxt_conf_vldt_object_t nxt_conf_vldt_share_action_members[] = {
- { nxt_string("share"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- { nxt_string("fallback"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_action,
- NULL },
+ {
+ .name = nxt_string("share"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("fallback"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_action,
+ },
NXT_CONF_VLDT_END
};
static nxt_conf_vldt_object_t nxt_conf_vldt_proxy_action_members[] = {
- { nxt_string("proxy"),
- NXT_CONF_VLDT_STRING,
- 0,
- &nxt_conf_vldt_proxy,
- NULL },
+ {
+ .name = nxt_string("proxy"),
+ .type = NXT_CONF_VLDT_STRING,
+ .validator = nxt_conf_vldt_proxy,
+ },
NXT_CONF_VLDT_END
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_route_members[] = {
- { nxt_string("match"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object,
- (void *) &nxt_conf_vldt_match_members },
-
- { nxt_string("action"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_action,
- NULL },
+static nxt_conf_vldt_object_t nxt_conf_vldt_external_members[] = {
+ {
+ .name = nxt_string("executable"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ }, {
+ .name = nxt_string("arguments"),
+ .type = NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_array_iterator,
+ .u.array = nxt_conf_vldt_argument,
+ },
- NXT_CONF_VLDT_END
+ NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[] = {
- { nxt_string("timeout"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("requests"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("shm"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
+static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
+ {
+ .name = nxt_string("home"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("path"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .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,
+ }, {
+ .name = nxt_string("threads"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ .validator = nxt_conf_vldt_threads,
+ }, {
+ .name = nxt_string("thread_stack_size"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ .validator = nxt_conf_vldt_thread_stack_size,
+ },
- NXT_CONF_VLDT_END
+ NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[] = {
- { nxt_string("spare"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("max"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("idle_timeout"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
+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,
+ .u.string = "root",
+ }, {
+ .name = nxt_string("script"),
+ .type = NXT_CONF_VLDT_ANY_TYPE,
+ .validator = nxt_conf_vldt_php_targets_exclusive,
+ .u.string = "script",
+ }, {
+ .name = nxt_string("index"),
+ .type = NXT_CONF_VLDT_ANY_TYPE,
+ .validator = nxt_conf_vldt_php_targets_exclusive,
+ .u.string = "index",
+ }, {
+ .name = nxt_string("targets"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_php_targets,
+ },
- NXT_CONF_VLDT_END
+ NXT_CONF_VLDT_NEXT(nxt_conf_vldt_php_common_members)
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_app_namespaces_members[] = {
-
-#if (NXT_HAVE_CLONE_NEWUSER)
- { nxt_string("credential"),
- NXT_CONF_VLDT_BOOLEAN,
- 0,
- NULL,
- NULL },
-#endif
-
-#if (NXT_HAVE_CLONE_NEWPID)
- { nxt_string("pid"),
- NXT_CONF_VLDT_BOOLEAN,
- 0,
- NULL,
- NULL },
-#endif
-
-#if (NXT_HAVE_CLONE_NEWNET)
- { nxt_string("network"),
- NXT_CONF_VLDT_BOOLEAN,
- 0,
- NULL,
- NULL },
-#endif
+static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[] = {
+ {
+ .name = nxt_string("options"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_php_options_members,
+ },
-#if (NXT_HAVE_CLONE_NEWNS)
- { nxt_string("mount"),
- NXT_CONF_VLDT_BOOLEAN,
- 0,
- NULL,
- NULL },
-#endif
+ NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
+};
-#if (NXT_HAVE_CLONE_NEWUTS)
- { nxt_string("uname"),
- NXT_CONF_VLDT_BOOLEAN,
- 0,
- NULL,
- NULL },
-#endif
-#if (NXT_HAVE_CLONE_NEWCGROUP)
- { nxt_string("cgroup"),
- NXT_CONF_VLDT_BOOLEAN,
- 0,
- NULL,
- NULL },
-#endif
+static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[] = {
+ {
+ .name = nxt_string("file"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("admin"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object_iterator,
+ .u.object = nxt_conf_vldt_php_option,
+ }, {
+ .name = nxt_string("user"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object_iterator,
+ .u.object = nxt_conf_vldt_php_option,
+ },
NXT_CONF_VLDT_END
};
-#if (NXT_HAVE_CLONE_NEWUSER)
-
-static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = {
- { nxt_string("container"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("host"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
-
- { nxt_string("size"),
- NXT_CONF_VLDT_INTEGER,
- 0,
- NULL,
- NULL },
+static nxt_conf_vldt_object_t nxt_conf_vldt_php_target_members[] = {
+ {
+ .name = nxt_string("root"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ }, {
+ .name = nxt_string("script"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("index"),
+ .type = NXT_CONF_VLDT_STRING,
+ },
NXT_CONF_VLDT_END
};
-#endif
+static nxt_conf_vldt_object_t nxt_conf_vldt_php_notargets_members[] = {
+ {
+ .name = nxt_string("root"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ }, {
+ .name = nxt_string("script"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("index"),
+ .type = NXT_CONF_VLDT_STRING,
+ },
-#if (NXT_HAVE_ISOLATION_ROOTFS)
-
-static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[] = {
- { nxt_string("language_deps"),
- NXT_CONF_VLDT_BOOLEAN,
- 0,
- NULL,
- NULL },
-
- NXT_CONF_VLDT_END
+ NXT_CONF_VLDT_NEXT(nxt_conf_vldt_php_common_members)
};
-#endif
+static nxt_conf_vldt_object_t nxt_conf_vldt_perl_members[] = {
+ {
+ .name = nxt_string("script"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ }, {
+ .name = nxt_string("threads"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ .validator = nxt_conf_vldt_threads,
+ }, {
+ .name = nxt_string("thread_stack_size"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ .validator = nxt_conf_vldt_thread_stack_size,
+ },
-static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = {
- { nxt_string("namespaces"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_clone_namespaces,
- (void *) &nxt_conf_vldt_app_namespaces_members },
+ NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
+};
-#if (NXT_HAVE_CLONE_NEWUSER)
- { nxt_string("uidmap"),
- NXT_CONF_VLDT_ARRAY,
- 0,
- &nxt_conf_vldt_array_iterator,
- (void *) &nxt_conf_vldt_clone_uidmap },
+static nxt_conf_vldt_object_t nxt_conf_vldt_ruby_members[] = {
+ {
+ .name = nxt_string("script"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ }, {
+ .name = nxt_string("threads"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ .validator = nxt_conf_vldt_threads,
+ },
- { nxt_string("gidmap"),
- NXT_CONF_VLDT_ARRAY,
- 0,
- &nxt_conf_vldt_array_iterator,
- (void *) &nxt_conf_vldt_clone_gidmap },
+ NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
+};
-#endif
-#if (NXT_HAVE_ISOLATION_ROOTFS)
+static nxt_conf_vldt_object_t nxt_conf_vldt_java_members[] = {
+ {
+ .name = nxt_string("classpath"),
+ .type = NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_array_iterator,
+ .u.array = nxt_conf_vldt_java_classpath,
+ }, {
+ .name = nxt_string("webapp"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED,
+ }, {
+ .name = nxt_string("options"),
+ .type = NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_array_iterator,
+ .u.array = nxt_conf_vldt_java_option,
+ }, {
+ .name = nxt_string("unit_jars"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("threads"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ .validator = nxt_conf_vldt_threads,
+ }, {
+ .name = nxt_string("thread_stack_size"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ .validator = nxt_conf_vldt_thread_stack_size,
+ },
- { nxt_string("rootfs"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
+ NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
+};
- { nxt_string("automount"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object,
- (void *) &nxt_conf_vldt_app_automount_members },
-#endif
+static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[] = {
+ {
+ .name = nxt_string("type"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("limits"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_app_limits_members,
+ }, {
+ .name = nxt_string("processes"),
+ .type = NXT_CONF_VLDT_INTEGER | NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_processes,
+ .u.members = nxt_conf_vldt_app_processes_members,
+ }, {
+ .name = nxt_string("user"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("group"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("working_directory"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("environment"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object_iterator,
+ .u.object = nxt_conf_vldt_environment,
+ }, {
+ .name = nxt_string("isolation"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_isolation,
+ .u.members = nxt_conf_vldt_app_isolation_members,
+ },
-#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
+ NXT_CONF_VLDT_END
+};
- { nxt_string("new_privs"),
- NXT_CONF_VLDT_BOOLEAN,
- 0,
- NULL,
- NULL },
-#endif
+static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[] = {
+ {
+ .name = nxt_string("timeout"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("requests"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("shm"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ },
NXT_CONF_VLDT_END
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[] = {
- { nxt_string("type"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- { nxt_string("limits"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object,
- (void *) &nxt_conf_vldt_app_limits_members },
-
- { nxt_string("processes"),
- NXT_CONF_VLDT_INTEGER | NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_processes,
- (void *) &nxt_conf_vldt_app_processes_members },
-
- { nxt_string("user"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- { nxt_string("group"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- { nxt_string("working_directory"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- { nxt_string("environment"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object_iterator,
- (void *) &nxt_conf_vldt_environment },
-
- { nxt_string("isolation"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_isolation,
- (void *) &nxt_conf_vldt_app_isolation_members },
+static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[] = {
+ {
+ .name = nxt_string("spare"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("max"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("idle_timeout"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ },
NXT_CONF_VLDT_END
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_external_members[] = {
- { nxt_string("executable"),
- NXT_CONF_VLDT_STRING,
- NXT_CONF_VLDT_REQUIRED,
- NULL,
- NULL },
-
- { nxt_string("arguments"),
- NXT_CONF_VLDT_ARRAY,
- 0,
- &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_app_isolation_members[] = {
+ {
+ .name = nxt_string("namespaces"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_clone_namespaces,
+ .u.members = nxt_conf_vldt_app_namespaces_members,
+ },
-static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
- { nxt_string("home"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- { nxt_string("path"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- { nxt_string("module"),
- NXT_CONF_VLDT_STRING,
- NXT_CONF_VLDT_REQUIRED,
- NULL,
- NULL },
-
- { nxt_string("callable"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members)
-};
+#if (NXT_HAVE_CLONE_NEWUSER)
+ {
+ .name = nxt_string("uidmap"),
+ .type = NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_array_iterator,
+ .u.array = nxt_conf_vldt_clone_uidmap,
+ }, {
+ .name = nxt_string("gidmap"),
+ .type = NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_array_iterator,
+ .u.array = nxt_conf_vldt_clone_gidmap,
+ },
+#endif
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+ {
+ .name = nxt_string("rootfs"),
+ .type = NXT_CONF_VLDT_STRING,
+ }, {
+ .name = nxt_string("automount"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_app_automount_members,
+ },
+#endif
-static nxt_conf_vldt_object_t nxt_conf_vldt_php_target_members[] = {
- { nxt_string("root"),
- NXT_CONF_VLDT_STRING,
- NXT_CONF_VLDT_REQUIRED,
- NULL,
- NULL },
-
- { nxt_string("script"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- { nxt_string("index"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
+#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
+ {
+ .name = nxt_string("new_privs"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ },
+#endif
NXT_CONF_VLDT_END
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[] = {
- { nxt_string("file"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- { nxt_string("admin"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object_iterator,
- (void *) &nxt_conf_vldt_php_option },
-
- { nxt_string("user"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object_iterator,
- (void *) &nxt_conf_vldt_php_option },
-
- NXT_CONF_VLDT_END
-};
+static nxt_conf_vldt_object_t nxt_conf_vldt_app_namespaces_members[] = {
+#if (NXT_HAVE_CLONE_NEWUSER)
+ {
+ .name = nxt_string("credential"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ },
+#endif
-static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[] = {
- { nxt_string("options"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object,
- (void *) &nxt_conf_vldt_php_options_members },
+#if (NXT_HAVE_CLONE_NEWPID)
+ {
+ .name = nxt_string("pid"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ },
+#endif
- NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members)
-};
+#if (NXT_HAVE_CLONE_NEWNET)
+ {
+ .name = nxt_string("network"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ },
+#endif
+#if (NXT_HAVE_CLONE_NEWNS)
+ {
+ .name = nxt_string("mount"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ },
+#endif
-static nxt_conf_vldt_object_t nxt_conf_vldt_php_notargets_members[] = {
- { nxt_string("root"),
- NXT_CONF_VLDT_STRING,
- NXT_CONF_VLDT_REQUIRED,
- NULL,
- NULL },
-
- { nxt_string("script"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- { nxt_string("index"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members)
-};
+#if (NXT_HAVE_CLONE_NEWUTS)
+ {
+ .name = nxt_string("uname"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ },
+#endif
+#if (NXT_HAVE_CLONE_NEWCGROUP)
+ {
+ .name = nxt_string("cgroup"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ },
+#endif
-static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = {
- { nxt_string("root"),
- NXT_CONF_VLDT_ANY_TYPE,
- 0,
- &nxt_conf_vldt_php_targets_exclusive,
- (void *) "root" },
-
- { nxt_string("script"),
- NXT_CONF_VLDT_ANY_TYPE,
- 0,
- &nxt_conf_vldt_php_targets_exclusive,
- (void *) "script" },
-
- { nxt_string("index"),
- NXT_CONF_VLDT_ANY_TYPE,
- 0,
- &nxt_conf_vldt_php_targets_exclusive,
- (void *) "index" },
-
- { nxt_string("targets"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_php_targets,
- NULL },
-
- NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members)
+ NXT_CONF_VLDT_END
};
-static nxt_conf_vldt_object_t nxt_conf_vldt_perl_members[] = {
- { nxt_string("script"),
- NXT_CONF_VLDT_STRING,
- NXT_CONF_VLDT_REQUIRED,
- NULL,
- NULL },
+#if (NXT_HAVE_ISOLATION_ROOTFS)
- NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members)
+static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[] = {
+ {
+ .name = nxt_string("language_deps"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ }, {
+ .name = nxt_string("tmpfs"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ }, {
+ .name = nxt_string("procfs"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ },
+
+ NXT_CONF_VLDT_END
};
+#endif
-static nxt_conf_vldt_object_t nxt_conf_vldt_ruby_members[] = {
- { nxt_string("script"),
- NXT_CONF_VLDT_STRING,
- NXT_CONF_VLDT_REQUIRED,
- NULL,
- NULL },
- NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members)
-};
+#if (NXT_HAVE_CLONE_NEWUSER)
+static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = {
+ {
+ .name = nxt_string("container"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("host"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ }, {
+ .name = nxt_string("size"),
+ .type = NXT_CONF_VLDT_INTEGER,
+ },
-static nxt_conf_vldt_object_t nxt_conf_vldt_java_members[] = {
- { nxt_string("classpath"),
- NXT_CONF_VLDT_ARRAY,
- 0,
- &nxt_conf_vldt_array_iterator,
- (void *) &nxt_conf_vldt_java_classpath },
-
- { nxt_string("webapp"),
- NXT_CONF_VLDT_STRING,
- NXT_CONF_VLDT_REQUIRED,
- NULL,
- NULL },
-
- { nxt_string("options"),
- NXT_CONF_VLDT_ARRAY,
- 0,
- &nxt_conf_vldt_array_iterator,
- (void *) &nxt_conf_vldt_java_option },
-
- { nxt_string("unit_jars"),
- NXT_CONF_VLDT_STRING,
- 0,
- NULL,
- NULL },
-
- NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members)
+ NXT_CONF_VLDT_END
};
+#endif
+
static nxt_conf_vldt_object_t nxt_conf_vldt_upstream_members[] = {
- { nxt_string("servers"),
- NXT_CONF_VLDT_OBJECT,
- 0,
- &nxt_conf_vldt_object_iterator,
- (void *) &nxt_conf_vldt_server },
+ {
+ .name = nxt_string("servers"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object_iterator,
+ .u.object = nxt_conf_vldt_server,
+ },
NXT_CONF_VLDT_END
};
static nxt_conf_vldt_object_t nxt_conf_vldt_upstream_server_members[] = {
- { nxt_string("weight"),
- NXT_CONF_VLDT_NUMBER,
- 0,
- &nxt_conf_vldt_server_weight,
- NULL },
+ {
+ .name = nxt_string("weight"),
+ .type = NXT_CONF_VLDT_NUMBER,
+ .validator = nxt_conf_vldt_server_weight,
+ },
NXT_CONF_VLDT_END
};
@@ -1440,6 +1377,72 @@ nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
static nxt_int_t
+nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data)
+{
+ nxt_str_t proto;
+
+ static const nxt_str_t wsgi = nxt_string("wsgi");
+ static const nxt_str_t asgi = nxt_string("asgi");
+
+ nxt_conf_get_string(value, &proto);
+
+ if (nxt_strstr_eq(&proto, &wsgi) || nxt_strstr_eq(&proto, &asgi)) {
+ return NXT_OK;
+ }
+
+ return nxt_conf_vldt_error(vldt, "The \"protocol\" can either be "
+ "\"wsgi\" or \"asgi\".");
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_threads(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
+ void *data)
+{
+ int64_t threads;
+
+ threads = nxt_conf_get_number(value);
+
+ if (threads < 1) {
+ return nxt_conf_vldt_error(vldt, "The \"threads\" number must be "
+ "equal to or greater than 1.");
+ }
+
+ if (threads > NXT_INT32_T_MAX) {
+ return nxt_conf_vldt_error(vldt, "The \"threads\" number must "
+ "not exceed %d.", NXT_INT32_T_MAX);
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_thread_stack_size(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data)
+{
+ int64_t size;
+
+ size = nxt_conf_get_number(value);
+
+ if (size < PTHREAD_STACK_MIN) {
+ return nxt_conf_vldt_error(vldt, "The \"thread_stack_size\" number "
+ "must be equal to or greater than %d.",
+ PTHREAD_STACK_MIN);
+ }
+
+ if ((size % nxt_pagesize) != 0) {
+ return nxt_conf_vldt_error(vldt, "The \"thread_stack_size\" number "
+ "must be a multiple of the system page size (%d).",
+ nxt_pagesize);
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
nxt_conf_vldt_routes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
void *data)
{
@@ -1502,8 +1505,12 @@ static nxt_int_t
nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value)
{
- nxt_str_t pattern;
- nxt_uint_t i, first, last;
+ nxt_str_t pattern;
+ nxt_uint_t i, first, last;
+#if (NXT_HAVE_REGEX)
+ nxt_regex_t *re;
+ nxt_regex_err_t err;
+#endif
if (nxt_conf_type(value) != NXT_CONF_STRING) {
return nxt_conf_vldt_error(vldt, "The \"match\" patterns for \"host\", "
@@ -1517,6 +1524,32 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
}
first = (pattern.start[0] == '!');
+
+ if (first < pattern.length && pattern.start[first] == '~') {
+#if (NXT_HAVE_REGEX)
+ pattern.start += first + 1;
+ pattern.length -= first + 1;
+
+ re = nxt_regex_compile(vldt->pool, &pattern, &err);
+ if (nxt_slow_path(re == NULL)) {
+ if (err.offset < pattern.length) {
+ return nxt_conf_vldt_error(vldt, "Invalid regular expression: "
+ "%s at offset %d",
+ err.msg, err.offset);
+ }
+
+ return nxt_conf_vldt_error(vldt, "Invalid regular expression: %s",
+ err.msg);
+ }
+
+ return NXT_OK;
+#else
+ return nxt_conf_vldt_error(vldt, "Unit is built without support of "
+ "regular expressions: \"--no-regex\" "
+ "./configure option was set.");
+#endif
+ }
+
last = pattern.length - 1;
for (i = first; i < last; i++) {
@@ -1887,8 +1920,8 @@ nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
for ( ;; ) {
if (vals->name.length == 0) {
- if (vals->data != NULL) {
- vals = vals->data;
+ if (vals->u.members != NULL) {
+ vals = vals->u.members;
continue;
}
@@ -1921,8 +1954,8 @@ nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
for ( ;; ) {
if (vals->name.length == 0) {
- if (vals->data != NULL) {
- vals = vals->data;
+ if (vals->u.members != NULL) {
+ vals = vals->u.members;
continue;
}
@@ -1942,7 +1975,7 @@ nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
}
if (vals->validator != NULL) {
- ret = vals->validator(vldt, member, vals->data);
+ ret = vals->validator(vldt, member, vals->u.members);
if (ret != NXT_OK) {
return ret;
diff --git a/src/nxt_external.c b/src/nxt_external.c
index 1adb839c..5703e294 100644
--- a/src/nxt_external.c
+++ b/src/nxt_external.c
@@ -101,18 +101,24 @@ nxt_external_start(nxt_task_t *task, nxt_process_data_t *data)
return NXT_ERROR;
}
+ rc = nxt_external_fd_no_cloexec(task, my_port->pair[1]);
+ 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;"
- "%PI,%ud,%d;"
+ "%PI,%ud,%d,%d;"
"%d,%z,%Z",
NXT_VERSION, my_port->process->stream,
main_port->pid, main_port->id, main_port->pair[1],
router_port->pid, router_port->id, router_port->pair[1],
my_port->pid, my_port->id, my_port->pair[0],
+ my_port->pair[1],
2, conf->shm_limit);
if (nxt_slow_path(p == end)) {
diff --git a/src/nxt_fs.c b/src/nxt_fs.c
index 0228c25a..71498f99 100644
--- a/src/nxt_fs.c
+++ b/src/nxt_fs.c
@@ -18,15 +18,59 @@ static nxt_int_t nxt_fs_mkdir(const u_char *dir, mode_t mode);
nxt_int_t
nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt)
{
- int rc;
+ int rc;
+ const char *fsname;
+ unsigned long flags;
- rc = mount((const char *) mnt->src, (const char *) mnt->dst,
- (const char *) mnt->fstype, mnt->flags, mnt->data);
+ flags = 0;
+
+ switch (mnt->type) {
+ case NXT_FS_BIND:
+ if (nxt_slow_path(mnt->flags != 0)) {
+ nxt_log(task, NXT_LOG_WARN,
+ "bind mount ignores additional flags");
+ }
+
+ fsname = "bind";
+ flags = MS_BIND | MS_REC;
+ break;
+
+ case NXT_FS_PROC:
+ fsname = "proc";
+ goto getflags;
+
+ case NXT_FS_TMP:
+ fsname = "tmpfs";
+ goto getflags;
+
+ default:
+ fsname = (const char *) mnt->name;
+
+ getflags:
+
+ if (mnt->flags & NXT_FS_FLAGS_NODEV) {
+ flags |= MS_NODEV;
+ }
+
+ if (mnt->flags & NXT_FS_FLAGS_NOEXEC) {
+ flags |= MS_NOEXEC;
+ }
+
+ if (mnt->flags & NXT_FS_FLAGS_NOSUID) {
+ flags |= MS_NOSUID;
+ }
+
+ if (!(mnt->flags & NXT_FS_FLAGS_NOTIME)) {
+ flags |= MS_RELATIME;
+ }
+ }
+
+ rc = mount((const char *) mnt->src, (const char *) mnt->dst, fsname, flags,
+ mnt->data);
if (nxt_slow_path(rc < 0)) {
- nxt_alert(task, "mount(\"%s\", \"%s\", \"%s\", %d, \"%s\") %E",
- mnt->src, mnt->dst, mnt->fstype, mnt->flags, mnt->data,
- nxt_errno);
+ nxt_alert(task, "mount(\"%s\", \"%s\", \"%s\", %ul, \"%s\") %E",
+ mnt->src, mnt->dst, fsname, flags, mnt->data, nxt_errno);
return NXT_ERROR;
}
@@ -34,37 +78,66 @@ nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt)
return NXT_OK;
}
-
#elif (NXT_HAVE_FREEBSD_NMOUNT)
nxt_int_t
nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt)
{
+ int flags;
u_char *data, *p, *end;
size_t iovlen;
nxt_int_t ret;
- const char *fstype;
+ const char *fsname;
struct iovec iov[128];
char errmsg[256];
- if (nxt_strncmp(mnt->fstype, "bind", 4) == 0) {
- fstype = "nullfs";
+ if (nxt_slow_path((mnt->flags & NXT_FS_FLAGS_NODEV) && !mnt->builtin)) {
+ nxt_alert(task, "nmount(2) doesn't support \"nodev\" option");
- } else if (nxt_strncmp(mnt->fstype, "proc", 4) == 0) {
- fstype = "procfs";
+ return NXT_ERROR;
+ }
- } else if (nxt_strncmp(mnt->fstype, "tmpfs", 5) == 0) {
- fstype = "tmpfs";
+ flags = 0;
- } else {
- nxt_alert(task, "mount type \"%s\" not implemented.", mnt->fstype);
- return NXT_ERROR;
+ switch (mnt->type) {
+ case NXT_FS_BIND:
+ fsname = "nullfs";
+ break;
+
+ case NXT_FS_PROC:
+ fsname = "procfs";
+ goto getflags;
+
+ case NXT_FS_TMP:
+ fsname = "tmpfs";
+ goto getflags;
+
+ default:
+ fsname = (const char *) mnt->name;
+
+ getflags:
+
+ if (mnt->flags & NXT_FS_FLAGS_NOEXEC) {
+ flags |= MNT_NOEXEC;
+ }
+
+ if (mnt->flags & NXT_FS_FLAGS_NOSUID) {
+ flags |= MNT_NOSUID;
+ }
+
+ if (mnt->flags & NXT_FS_FLAGS_NOTIME) {
+ flags |= MNT_NOATIME;
+ }
+
+ if (mnt->flags & NXT_FS_FLAGS_RDONLY) {
+ flags |= MNT_RDONLY;
+ }
}
iov[0].iov_base = (void *) "fstype";
iov[0].iov_len = 7;
- iov[1].iov_base = (void *) fstype;
- iov[1].iov_len = nxt_strlen(fstype) + 1;
+ iov[1].iov_base = (void *) fsname;
+ iov[1].iov_len = nxt_strlen(fsname) + 1;
iov[2].iov_base = (void *) "fspath";
iov[2].iov_len = 7;
iov[3].iov_base = (void *) mnt->dst;
@@ -99,8 +172,10 @@ nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt)
*end = '\0';
- iov[iovlen++].iov_base = (void *) p;
- iov[iovlen++].iov_len = (end - p) + 1;
+ iov[iovlen].iov_base = (void *) p;
+ iov[iovlen].iov_len = (end - p) + 1;
+
+ iovlen++;
p = end + 1;
@@ -109,15 +184,17 @@ nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt)
*end = '\0';
}
- iov[iovlen++].iov_base = (void *) p;
- iov[iovlen++].iov_len = nxt_strlen(p) + 1;
+ iov[iovlen].iov_base = (void *) p;
+ iov[iovlen].iov_len = nxt_strlen(p) + 1;
+
+ iovlen++;
} while (end != NULL && nxt_nitems(iov) > (iovlen + 2));
}
ret = NXT_OK;
- if (nxt_slow_path(nmount(iov, iovlen, 0) < 0)) {
+ if (nxt_slow_path(nmount(iov, iovlen, flags) < 0)) {
nxt_alert(task, "nmount(%p, %d, 0) %s", iov, iovlen, errmsg);
ret = NXT_ERROR;
}
diff --git a/src/nxt_fs.h b/src/nxt_fs.h
index bbd7ab9f..ff589979 100644
--- a/src/nxt_fs.h
+++ b/src/nxt_fs.h
@@ -6,50 +6,33 @@
#define _NXT_FS_H_INCLUDED_
-#ifdef MS_BIND
-#define NXT_MS_BIND MS_BIND
-#else
-#define NXT_MS_BIND 0
-#endif
-
-#ifdef MS_REC
-#define NXT_MS_REC MS_BIND
-#else
-#define NXT_MS_REC 0
-#endif
-
-#ifdef MS_NOSUID
-#define NXT_MS_NOSUID MS_NOSUID
-#else
-#define NXT_MS_NOSUID 0
-#endif
-
-#ifdef MS_NOEXEC
-#define NXT_MS_NOEXEC MS_NOEXEC
-#else
-#define NXT_MS_NOEXEC 0
-#endif
-
-#ifdef MS_RELATIME
-#define NXT_MS_RELATIME MS_RELATIME
-#else
-#define NXT_MS_RELATIME 0
-#endif
-
-#ifdef MS_NODEV
-#define NXT_MS_NODEV MS_NODEV
-#else
-#define NXT_MS_NODEV 0
-#endif
+typedef enum {
+ NXT_FS_UNKNOWN = 0,
+ NXT_FS_BIND,
+ NXT_FS_TMP,
+ NXT_FS_PROC,
+ NXT_FS_LAST,
+} nxt_fs_type_t;
+
+
+typedef enum {
+ NXT_FS_FLAGS_NOSUID = 1 << 0,
+ NXT_FS_FLAGS_NOEXEC = 1 << 1,
+ NXT_FS_FLAGS_NOTIME = 1 << 2,
+ NXT_FS_FLAGS_NODEV = 1 << 3,
+ NXT_FS_FLAGS_RDONLY = 1 << 4,
+} nxt_fs_flags_t;
typedef struct {
- u_char *src;
- u_char *dst;
- u_char *fstype;
- nxt_int_t flags;
- u_char *data;
- nxt_uint_t builtin; /* 1-bit */
+ u_char *src;
+ u_char *dst;
+ nxt_fs_type_t type;
+ u_char *name;
+ nxt_fs_flags_t flags;
+ u_char *data;
+ nxt_uint_t builtin; /* 1-bit */
+ nxt_uint_t deps; /* 1-bit */
} nxt_fs_mount_t;
diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c
index dc23d7c4..dccbe56c 100644
--- a/src/nxt_h1proto.c
+++ b/src/nxt_h1proto.c
@@ -467,6 +467,7 @@ nxt_h1p_conn_request_init(nxt_task_t *task, void *obj, void *data)
nxt_int_t ret;
nxt_conn_t *c;
nxt_h1proto_t *h1p;
+ nxt_socket_conf_t *skcf;
nxt_http_request_t *r;
nxt_socket_conf_joint_t *joint;
@@ -503,11 +504,14 @@ nxt_h1p_conn_request_init(nxt_task_t *task, void *obj, void *data)
joint->count++;
r->conf = joint;
+ skcf = joint->socket_conf;
if (c->local == NULL) {
- c->local = joint->socket_conf->sockaddr;
+ c->local = skcf->sockaddr;
}
+ h1p->parser.discard_unsafe_fields = skcf->discard_unsafe_fields;
+
nxt_h1p_conn_request_header_parse(task, c, h1p);
return;
}
diff --git a/src/nxt_http.h b/src/nxt_http.h
index 08181520..1418be95 100644
--- a/src/nxt_http.h
+++ b/src/nxt_http.h
@@ -7,6 +7,8 @@
#ifndef _NXT_HTTP_H_INCLUDED_
#define _NXT_HTTP_H_INCLUDED_
+#include <nxt_regex.h>
+
typedef enum {
NXT_HTTP_UNSET = -1,
@@ -168,6 +170,10 @@ struct nxt_http_request_s {
void *req_rpc_data;
+#if (NXT_HAVE_REGEX)
+ nxt_regex_match_t *regex_match;
+#endif
+
nxt_http_peer_t *peer;
nxt_buf_t *last;
diff --git a/src/nxt_http_parse.c b/src/nxt_http_parse.c
index 22004cc1..338b0a90 100644
--- a/src/nxt_http_parse.c
+++ b/src/nxt_http_parse.c
@@ -288,11 +288,13 @@ continue_target:
case NXT_HTTP_TARGET_SPACE:
rp->target_end = p;
goto space_after_target;
-
+#if 0
case NXT_HTTP_TARGET_QUOTE_MARK:
rp->quoted_target = 1;
goto rest_of_target;
-
+#else
+ case NXT_HTTP_TARGET_QUOTE_MARK:
+#endif
case NXT_HTTP_TARGET_HASH:
rp->complex_target = 1;
goto rest_of_target;
@@ -378,7 +380,7 @@ space_after_target:
}
}
- rp->space_in_target = 1;
+ //rp->space_in_target = 1;
if (rest) {
goto rest_of_target;
@@ -397,7 +399,7 @@ space_after_target:
goto space_after_target;
}
- rp->space_in_target = 1;
+ //rp->space_in_target = 1;
if (rest) {
goto rest_of_target;
@@ -432,7 +434,12 @@ space_after_target:
*pos = p + 10;
}
- if (rp->complex_target != 0 || rp->quoted_target != 0) {
+ if (rp->complex_target != 0
+#if 0
+ || rp->quoted_target != 0
+#endif
+ )
+ {
rc = nxt_http_parse_complex_target(rp);
if (nxt_slow_path(rc != NXT_OK)) {
@@ -518,11 +525,13 @@ nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos,
static const u_char normal[256] nxt_aligned(64) =
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
+ /* \s ! " # $ % & ' ( ) * + , . / : ; < = > ? */
+ "\0\1\0\1\1\1\1\1\0\0\1\1\0" "-" "\1\0" "0123456789" "\0\0\0\0\0\0"
- /* These 64 bytes should reside in one cache line. */
- "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0_"
- "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
+ /* @ [ \ ] ^ _ */
+ "\0" "abcdefghijklmnopqrstuvwxyz" "\0\0\0\1\1"
+ /* ` { | } ~ */
+ "\1" "abcdefghijklmnopqrstuvwxyz" "\0\1\0\1\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
@@ -538,9 +547,14 @@ nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos,
\
c = normal[ch]; \
\
- if (nxt_slow_path(c == '\0')) { \
- p = &(ch); \
- goto name_end; \
+ if (nxt_slow_path(c <= '\1')) { \
+ if (c == '\0') { \
+ p = &(ch); \
+ goto name_end; \
+ } \
+ \
+ rp->skip_field = rp->discard_unsafe_fields; \
+ c = ch; \
} \
\
hash = nxt_http_field_hash_char(hash, c);
@@ -777,20 +791,25 @@ nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos,
*pos = p + 1;
if (rp->field_name.length != 0) {
- field = nxt_list_add(rp->fields);
+ if (rp->skip_field) {
+ rp->skip_field = 0;
- if (nxt_slow_path(field == NULL)) {
- return NXT_ERROR;
- }
+ } else {
+ field = nxt_list_add(rp->fields);
- field->hash = nxt_http_field_hash_end(rp->field_hash);
- field->skip = 0;
- field->hopbyhop = 0;
+ if (nxt_slow_path(field == NULL)) {
+ return NXT_ERROR;
+ }
- field->name_length = rp->field_name.length;
- field->value_length = rp->field_value.length;
- field->name = rp->field_name.start;
- field->value = rp->field_value.start;
+ field->hash = nxt_http_field_hash_end(rp->field_hash);
+ field->skip = 0;
+ field->hopbyhop = 0;
+
+ field->name_length = rp->field_name.length;
+ field->value_length = rp->field_value.length;
+ field->name = rp->field_name.start;
+ field->value = rp->field_value.start;
+ }
rp->field_hash = NXT_HTTP_FIELD_HASH_INIT;
@@ -1023,7 +1042,7 @@ nxt_http_parse_complex_target(nxt_http_request_parse_t *rp)
break;
case sw_quoted:
- rp->quoted_target = 1;
+ //rp->quoted_target = 1;
if (ch >= '0' && ch <= '9') {
high = (u_char) (ch - '0');
diff --git a/src/nxt_http_parse.h b/src/nxt_http_parse.h
index cbfc8433..3cd9bd15 100644
--- a/src/nxt_http_parse.h
+++ b/src/nxt_http_parse.h
@@ -55,15 +55,19 @@ struct nxt_http_request_parse_s {
uint32_t field_hash;
+ uint8_t skip_field; /* 1 bit */
+ uint8_t discard_unsafe_fields; /* 1 bit */
+
/* target with "/." */
- uint8_t complex_target; /* 1 bit */
+ uint8_t complex_target; /* 1 bit */
+#if 0
/* target with "%" */
- uint8_t quoted_target; /* 1 bit */
+ uint8_t quoted_target; /* 1 bit */
/* target with " " */
- uint8_t space_in_target; /* 1 bit */
-
+ uint8_t space_in_target; /* 1 bit */
+#endif
/* Preserve encoded '/' (%2F) and '%' (%25). */
- uint8_t encoded_slashes; /* 1 bit */
+ uint8_t encoded_slashes; /* 1 bit */
};
diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c
index 76fb3427..650c1a89 100644
--- a/src/nxt_http_request.c
+++ b/src/nxt_http_request.c
@@ -220,7 +220,7 @@ nxt_http_request_create(nxt_task_t *task)
nxt_buf_t *last;
nxt_http_request_t *r;
- mp = nxt_mp_create(1024, 128, 256, 32);
+ mp = nxt_mp_create(4096, 128, 512, 32);
if (nxt_slow_path(mp == NULL)) {
return NULL;
}
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index ae91076a..9aaa708e 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -8,6 +8,7 @@
#include <nxt_http.h>
#include <nxt_sockaddr.h>
#include <nxt_http_route_addr.h>
+#include <nxt_regex.h>
typedef enum {
@@ -76,12 +77,20 @@ typedef struct {
typedef struct {
+ union {
+ nxt_array_t *pattern_slices;
+#if (NXT_HAVE_REGEX)
+ nxt_regex_t *regex;
+#endif
+ } u;
uint32_t min_length;
- nxt_array_t *pattern_slices;
uint8_t case_sensitive; /* 1 bit */
uint8_t negative; /* 1 bit */
uint8_t any; /* 1 bit */
+#if (NXT_HAVE_REGEX)
+ uint8_t regex; /* 1 bit */
+#endif
} nxt_http_route_pattern_t;
@@ -1060,24 +1069,24 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
nxt_str_t test, tmp;
nxt_int_t ret;
nxt_array_t *slices;
+#if (NXT_HAVE_REGEX)
+ nxt_regex_t *re;
+ nxt_regex_err_t err;
+#endif
nxt_http_route_pattern_type_t type;
-
nxt_http_route_pattern_slice_t *slice;
type = NXT_HTTP_ROUTE_PATTERN_EXACT;
nxt_conf_get_string(cv, &test);
- slices = nxt_array_create(mp, 1, sizeof(nxt_http_route_pattern_slice_t));
- if (nxt_slow_path(slices == NULL)) {
- return NXT_ERROR;
- }
-
- pattern->pattern_slices = slices;
-
+ pattern->u.pattern_slices = NULL;
pattern->negative = 0;
pattern->any = 1;
pattern->min_length = 0;
+#if (NXT_HAVE_REGEX)
+ pattern->regex = 0;
+#endif
if (test.length != 0 && test.start[0] == '!') {
test.start++;
@@ -1087,6 +1096,41 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
pattern->any = 0;
}
+ if (test.length > 0 && test.start[0] == '~') {
+#if (NXT_HAVE_REGEX)
+ test.start++;
+ test.length--;
+
+ re = nxt_regex_compile(mp, &test, &err);
+ if (nxt_slow_path(re == NULL)) {
+ if (err.offset < test.length) {
+ nxt_alert(task, "nxt_regex_compile(%V) failed: %s at offset %d",
+ &test, err.msg, (int) err.offset);
+ return NXT_ERROR;
+ }
+
+ nxt_alert(task, "nxt_regex_compile(%V) failed %s", &test, err.msg);
+
+ return NXT_ERROR;
+ }
+
+ pattern->u.regex = re;
+ pattern->regex = 1;
+
+ return NXT_OK;
+
+#else
+ return NXT_ERROR;
+#endif
+ }
+
+ slices = nxt_array_create(mp, 1, sizeof(nxt_http_route_pattern_slice_t));
+ if (nxt_slow_path(slices == NULL)) {
+ return NXT_ERROR;
+ }
+
+ pattern->u.pattern_slices = slices;
+
if (test.length == 0) {
slice = nxt_array_add(slices);
if (nxt_slow_path(slice == NULL)) {
@@ -1980,6 +2024,9 @@ nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
}
ret = nxt_http_route_test_rule(r, rule, f->value, f->value_length);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return NXT_ERROR;
+ }
if (ret == 0) {
return ret;
@@ -2155,7 +2202,7 @@ static nxt_int_t
nxt_http_route_test_argument(nxt_http_request_t *r,
nxt_http_route_rule_t *rule, nxt_array_t *array)
{
- nxt_bool_t ret;
+ nxt_int_t ret;
nxt_http_name_value_t *nv, *end;
ret = 0;
@@ -2171,6 +2218,10 @@ nxt_http_route_test_argument(nxt_http_request_t *r,
{
ret = nxt_http_route_test_rule(r, rule, nv->value,
nv->value_length);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return NXT_ERROR;
+ }
+
if (ret == 0) {
break;
}
@@ -2189,7 +2240,7 @@ nxt_http_route_scheme(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
nxt_bool_t tls, https;
nxt_http_route_pattern_slice_t *pattern_slice;
- pattern_slice = rule->pattern[0].pattern_slices->elts;
+ pattern_slice = rule->pattern[0].u.pattern_slices->elts;
https = (pattern_slice->length == nxt_length("https"));
tls = (r->tls != NULL);
@@ -2337,7 +2388,7 @@ static nxt_int_t
nxt_http_route_test_cookie(nxt_http_request_t *r,
nxt_http_route_rule_t *rule, nxt_array_t *array)
{
- nxt_bool_t ret;
+ nxt_int_t ret;
nxt_http_name_value_t *nv, *end;
ret = 0;
@@ -2353,6 +2404,10 @@ nxt_http_route_test_cookie(nxt_http_request_t *r,
{
ret = nxt_http_route_test_rule(r, rule, nv->value,
nv->value_length);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return NXT_ERROR;
+ }
+
if (ret == 0) {
break;
}
@@ -2378,6 +2433,9 @@ nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule,
while (pattern < end) {
ret = nxt_http_route_pattern(r, pattern, start, length);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return NXT_ERROR;
+ }
/* nxt_http_route_pattern() returns either 1 or 0. */
ret ^= pattern->negative;
@@ -2403,11 +2461,26 @@ nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern,
nxt_array_t *pattern_slices;
nxt_http_route_pattern_slice_t *pattern_slice;
+#if (NXT_HAVE_REGEX)
+ if (pattern->regex) {
+ if (r->regex_match == NULL) {
+ r->regex_match = nxt_regex_match_create(r->mem_pool, 0);
+ if (nxt_slow_path(r->regex_match == NULL)) {
+ return NXT_ERROR;
+ }
+ }
+
+ return nxt_regex_match(pattern->u.regex, start, length, r->regex_match);
+ }
+#endif
+
if (length < pattern->min_length) {
return 0;
}
- pattern_slices = pattern->pattern_slices;
+ nxt_assert(pattern->u.pattern_slices != NULL);
+
+ pattern_slices = pattern->u.pattern_slices;
pattern_slice = pattern_slices->elts;
end = start + length;
diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c
index ac7a37e8..1e6323bc 100644
--- a/src/nxt_isolation.c
+++ b/src/nxt_isolation.c
@@ -41,6 +41,8 @@ static nxt_int_t nxt_isolation_set_mounts(nxt_task_t *task,
nxt_process_t *process, nxt_str_t *app_type);
static nxt_int_t nxt_isolation_set_lang_mounts(nxt_task_t *task,
nxt_process_t *process, nxt_array_t *syspaths);
+static int nxt_cdecl nxt_isolation_mount_compare(const void *v1,
+ const void *v2);
static void nxt_isolation_unmount_all(nxt_task_t *task, nxt_process_t *process);
#if (NXT_HAVE_PIVOT_ROOT) && (NXT_HAVE_CLONE_NEWNS)
@@ -85,15 +87,6 @@ nxt_isolation_main_prefork(nxt_task_t *task, nxt_process_t *process,
}
#endif
-#if (NXT_HAVE_ISOLATION_ROOTFS)
- if (process->isolation.rootfs != NULL) {
- ret = nxt_isolation_set_mounts(task, process, &app_conf->type);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
- }
-#endif
-
if (cap_setid) {
ret = nxt_process_creds_set(task, process, &app_conf->user,
&app_conf->group);
@@ -124,6 +117,29 @@ nxt_isolation_main_prefork(nxt_task_t *task, nxt_process_t *process,
}
}
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+ if (process->isolation.rootfs != NULL) {
+ nxt_int_t has_mnt;
+
+ ret = nxt_isolation_set_mounts(task, process, &app_conf->type);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ has_mnt = 0;
+
+#if (NXT_HAVE_CLONE_NEWNS)
+ has_mnt = nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS);
+#endif
+
+ if (process->user_cred->uid == 0 && !has_mnt) {
+ nxt_log(task, NXT_LOG_WARN,
+ "setting user \"root\" with \"rootfs\" is unsafe without "
+ "\"mount\" namespace isolation");
+ }
+ }
+#endif
+
#if (NXT_HAVE_CLONE_NEWUSER)
ret = nxt_isolation_vldt_creds(task, process);
if (nxt_slow_path(ret != NXT_OK)) {
@@ -468,10 +484,14 @@ nxt_isolation_set_automount(nxt_task_t *task, nxt_conf_value_t *isolation,
static nxt_str_t automount_name = nxt_string("automount");
static nxt_str_t langdeps_name = nxt_string("language_deps");
+ static nxt_str_t tmp_name = nxt_string("tmpfs");
+ static nxt_str_t proc_name = nxt_string("procfs");
automount = &process->isolation.automount;
automount->language_deps = 1;
+ automount->tmpfs = 1;
+ automount->procfs = 1;
conf = nxt_conf_get_object_member(isolation, &automount_name, NULL);
if (conf != NULL) {
@@ -479,6 +499,16 @@ nxt_isolation_set_automount(nxt_task_t *task, nxt_conf_value_t *isolation,
if (value != NULL) {
automount->language_deps = nxt_conf_get_boolean(value);
}
+
+ value = nxt_conf_get_object_member(conf, &tmp_name, NULL);
+ if (value != NULL) {
+ automount->tmpfs = nxt_conf_get_boolean(value);
+ }
+
+ value = nxt_conf_get_object_member(conf, &proc_name, NULL);
+ if (value != NULL) {
+ automount->procfs = nxt_conf_get_boolean(value);
+ }
}
return NXT_OK;
@@ -560,39 +590,41 @@ nxt_isolation_set_lang_mounts(nxt_task_t *task, nxt_process_t *process,
*p = '\0';
}
- mnt = nxt_array_add(mounts);
- if (nxt_slow_path(mnt == NULL)) {
- return NXT_ERROR;
- }
+ if (process->isolation.automount.tmpfs) {
+ mnt = nxt_array_add(mounts);
+ if (nxt_slow_path(mnt == NULL)) {
+ return NXT_ERROR;
+ }
- mnt->src = (u_char *) "tmpfs";
- mnt->fstype = (u_char *) "tmpfs";
- mnt->flags = NXT_MS_NOSUID | NXT_MS_NODEV | NXT_MS_NOEXEC | NXT_MS_RELATIME;
- mnt->data = (u_char *) "size=1m,mode=777";
- mnt->builtin = 1;
+ mnt->src = (u_char *) "tmpfs";
+ mnt->name = (u_char *) "tmpfs";
+ mnt->type = NXT_FS_TMP;
+ mnt->flags = (NXT_FS_FLAGS_NOSUID
+ | NXT_FS_FLAGS_NODEV
+ | NXT_FS_FLAGS_NOEXEC);
+ mnt->data = (u_char *) "size=1m,mode=777";
+ mnt->builtin = 1;
+ mnt->deps = 0;
+
+ mnt->dst = nxt_mp_nget(mp, rootfs_len + nxt_length("/tmp") + 1);
+ if (nxt_slow_path(mnt->dst == NULL)) {
+ return NXT_ERROR;
+ }
- mnt->dst = nxt_mp_nget(mp, rootfs_len + nxt_length("/tmp") + 1);
- if (nxt_slow_path(mnt->dst == NULL)) {
- return NXT_ERROR;
+ p = nxt_cpymem(mnt->dst, rootfs, rootfs_len);
+ p = nxt_cpymem(p, "/tmp", 4);
+ *p = '\0';
}
- p = nxt_cpymem(mnt->dst, rootfs, rootfs_len);
- p = nxt_cpymem(p, "/tmp", 4);
- *p = '\0';
-
-#if (NXT_HAVE_CLONE_NEWPID) && (NXT_HAVE_CLONE_NEWNS)
-
- if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWPID)
- && nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS))
- {
+ if (process->isolation.automount.procfs) {
mnt = nxt_array_add(mounts);
if (nxt_slow_path(mnt == NULL)) {
return NXT_ERROR;
}
- mnt->fstype = (u_char *) "proc";
- mnt->src = (u_char *) "proc";
-
+ mnt->name = (u_char *) "proc";
+ mnt->type = NXT_FS_PROC;
+ mnt->src = (u_char *) "none";
mnt->dst = nxt_mp_nget(mp, rootfs_len + nxt_length("/proc") + 1);
if (nxt_slow_path(mnt->dst == NULL)) {
return NXT_ERROR;
@@ -603,9 +635,13 @@ nxt_isolation_set_lang_mounts(nxt_task_t *task, nxt_process_t *process,
*p = '\0';
mnt->data = (u_char *) "";
- mnt->flags = 0;
+ mnt->flags = NXT_FS_FLAGS_NOEXEC | NXT_FS_FLAGS_NOSUID;
+ mnt->builtin = 1;
+ mnt->deps = 0;
}
-#endif
+
+ qsort(mounts->elts, mounts->nelts, sizeof(nxt_fs_mount_t),
+ nxt_isolation_mount_compare);
process->isolation.mounts = mounts;
@@ -613,14 +649,39 @@ nxt_isolation_set_lang_mounts(nxt_task_t *task, nxt_process_t *process,
}
+static int nxt_cdecl
+nxt_isolation_mount_compare(const void *v1, const void *v2)
+{
+ const nxt_fs_mount_t *mnt1, *mnt2;
+
+ mnt1 = v1;
+ mnt2 = v2;
+
+ return nxt_strlen(mnt1->src) > nxt_strlen(mnt2->src);
+}
+
+
void
nxt_isolation_unmount_all(nxt_task_t *task, nxt_process_t *process)
{
- size_t i, n;
+ size_t n;
nxt_array_t *mounts;
+ nxt_runtime_t *rt;
nxt_fs_mount_t *mnt;
nxt_process_automount_t *automount;
+ rt = task->thread->runtime;
+
+ if (!rt->capabilities.setid) {
+ return;
+ }
+
+#if (NXT_HAVE_CLONE_NEWNS)
+ if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS)) {
+ return;
+ }
+#endif
+
nxt_debug(task, "unmount all (%s)", process->name);
automount = &process->isolation.automount;
@@ -628,12 +689,14 @@ nxt_isolation_unmount_all(nxt_task_t *task, nxt_process_t *process)
n = mounts->nelts;
mnt = mounts->elts;
- for (i = 0; i < n; i++) {
- if (mnt[i].builtin && !automount->language_deps) {
+ while (n > 0) {
+ n--;
+
+ if (mnt[n].deps && !automount->language_deps) {
continue;
}
- nxt_fs_unmount(mnt[i].dst);
+ nxt_fs_unmount(mnt[n].dst);
}
}
@@ -658,11 +721,11 @@ nxt_isolation_prepare_rootfs(nxt_task_t *task, nxt_process_t *process)
for (i = 0; i < n; i++) {
dst = mnt[i].dst;
- if (mnt[i].builtin && !automount->language_deps) {
+ if (mnt[i].deps && !automount->language_deps) {
continue;
}
- if (nxt_slow_path(nxt_memcmp(mnt[i].fstype, "bind", 4) == 0
+ if (nxt_slow_path(mnt[i].type == NXT_FS_BIND
&& stat((const char *) mnt[i].src, &st) != 0))
{
nxt_log(task, NXT_LOG_WARN, "host path not found: %s", mnt[i].src);
diff --git a/src/nxt_java.c b/src/nxt_java.c
index 1f8864bd..ac715c0b 100644
--- a/src/nxt_java.c
+++ b/src/nxt_java.c
@@ -36,6 +36,11 @@ static nxt_int_t nxt_java_start(nxt_task_t *task,
static void nxt_java_request_handler(nxt_unit_request_info_t *req);
static void nxt_java_websocket_handler(nxt_unit_websocket_frame_t *ws);
static void nxt_java_close_handler(nxt_unit_request_info_t *req);
+static int nxt_java_ready_handler(nxt_unit_ctx_t *ctx);
+static void *nxt_java_thread_func(void *main_ctx);
+static int nxt_java_init_threads(nxt_java_app_conf_t *c);
+static void nxt_java_join_threads(nxt_unit_ctx_t *ctx,
+ nxt_java_app_conf_t *c);
static uint32_t compat[] = {
NXT_VERNUM, NXT_DEBUG,
@@ -43,6 +48,9 @@ static uint32_t compat[] = {
char *nxt_java_modules;
+static pthread_t *nxt_java_threads;
+static pthread_attr_t *nxt_java_thread_attr;
+
#define NXT_STRING(x) _NXT_STRING(x)
#define _NXT_STRING(x) #x
@@ -59,8 +67,10 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = {
};
typedef struct {
- JNIEnv *env;
- jobject ctx;
+ JavaVM *jvm;
+ jobject cl;
+ jobject ctx;
+ nxt_java_app_conf_t *conf;
} nxt_java_data_t;
@@ -402,8 +412,10 @@ nxt_java_start(nxt_task_t *task, nxt_process_data_t *data)
goto env_failed;
}
- java_data.env = env;
+ java_data.jvm = jvm;
+ java_data.cl = cl;
java_data.ctx = nxt_java_startContext(env, c->webapp, classpath);
+ java_data.conf = c;
if ((*env)->ExceptionCheck(env)) {
nxt_alert(task, "Unhandled exception in application start");
@@ -411,13 +423,20 @@ nxt_java_start(nxt_task_t *task, nxt_process_data_t *data)
return NXT_ERROR;
}
+ rc = nxt_java_init_threads(c);
+ if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
+ return NXT_ERROR;
+ }
+
nxt_unit_default_init(task, &java_init);
java_init.callbacks.request_handler = nxt_java_request_handler;
java_init.callbacks.websocket_handler = nxt_java_websocket_handler;
java_init.callbacks.close_handler = nxt_java_close_handler;
+ java_init.callbacks.ready_handler = nxt_java_ready_handler;
java_init.request_data_size = sizeof(nxt_java_request_data_t);
java_init.data = &java_data;
+ java_init.ctx_data = env;
java_init.shm_limit = app_conf->shm_limit;
ctx = nxt_unit_init(&java_init);
@@ -427,9 +446,8 @@ nxt_java_start(nxt_task_t *task, nxt_process_data_t *data)
}
rc = nxt_unit_run(ctx);
- if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- /* TODO report error */
- }
+
+ nxt_java_join_threads(ctx, c);
nxt_java_stopContext(env, java_data.ctx);
@@ -441,7 +459,7 @@ nxt_java_start(nxt_task_t *task, nxt_process_data_t *data)
(*jvm)->DestroyJavaVM(jvm);
- exit(0);
+ exit(rc);
return NXT_OK;
@@ -464,7 +482,7 @@ nxt_java_request_handler(nxt_unit_request_info_t *req)
nxt_java_request_data_t *data;
java_data = req->unit->data;
- env = java_data->env;
+ env = req->ctx->data;
data = req->data;
jreq = nxt_java_newRequest(env, java_data->ctx, req);
@@ -543,11 +561,9 @@ nxt_java_websocket_handler(nxt_unit_websocket_frame_t *ws)
void *b;
JNIEnv *env;
jobject jbuf;
- nxt_java_data_t *java_data;
nxt_java_request_data_t *data;
- java_data = ws->req->unit->data;
- env = java_data->env;
+ env = ws->req->ctx->data;
data = ws->req->data;
b = malloc(ws->payload_len);
@@ -578,11 +594,9 @@ static void
nxt_java_close_handler(nxt_unit_request_info_t *req)
{
JNIEnv *env;
- nxt_java_data_t *java_data;
nxt_java_request_data_t *data;
- java_data = req->unit->data;
- env = java_data->env;
+ env = req->ctx->data;
data = req->data;
nxt_java_Request_close(env, data->jreq);
@@ -593,3 +607,160 @@ nxt_java_close_handler(nxt_unit_request_info_t *req)
nxt_unit_request_done(req, NXT_UNIT_OK);
}
+
+static int
+nxt_java_ready_handler(nxt_unit_ctx_t *ctx)
+{
+ int res;
+ uint32_t i;
+ nxt_java_data_t *java_data;
+ nxt_java_app_conf_t *c;
+
+ /* Worker thread context. */
+ if (!nxt_unit_is_main_ctx(ctx)) {
+ return NXT_UNIT_OK;
+ }
+
+ java_data = ctx->unit->data;
+ c = java_data->conf;
+
+ if (c->threads <= 1) {
+ return NXT_UNIT_OK;
+ }
+
+ for (i = 0; i < c->threads - 1; i++) {
+ res = pthread_create(&nxt_java_threads[i], nxt_java_thread_attr,
+ nxt_java_thread_func, ctx);
+
+ if (nxt_fast_path(res == 0)) {
+ nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1));
+
+ } else {
+ nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)",
+ (int) (i + 1), strerror(res), res);
+
+ return NXT_UNIT_ERROR;
+ }
+ }
+
+ return NXT_UNIT_OK;
+}
+
+
+static void *
+nxt_java_thread_func(void *data)
+{
+ int rc;
+ JavaVM *jvm;
+ JNIEnv *env;
+ nxt_unit_ctx_t *main_ctx, *ctx;
+ nxt_java_data_t *java_data;
+
+ main_ctx = data;
+
+ nxt_unit_debug(main_ctx, "worker thread start");
+
+ java_data = main_ctx->unit->data;
+ jvm = java_data->jvm;
+
+ rc = (*jvm)->AttachCurrentThread(jvm, (void **) &env, NULL);
+ if (rc != JNI_OK) {
+ nxt_unit_alert(main_ctx, "failed to attach Java VM: %d", (int) rc);
+ return NULL;
+ }
+
+ nxt_java_setContextClassLoader(env, java_data->cl);
+
+ ctx = nxt_unit_ctx_alloc(main_ctx, env);
+ if (nxt_slow_path(ctx == NULL)) {
+ goto fail;
+ }
+
+ (void) nxt_unit_run(ctx);
+
+ nxt_unit_done(ctx);
+
+fail:
+
+ (*jvm)->DetachCurrentThread(jvm);
+
+ nxt_unit_debug(NULL, "worker thread end");
+
+ return NULL;
+}
+
+
+static int
+nxt_java_init_threads(nxt_java_app_conf_t *c)
+{
+ int res;
+ static pthread_attr_t attr;
+
+ if (c->threads <= 1) {
+ return NXT_UNIT_OK;
+ }
+
+ if (c->thread_stack_size > 0) {
+ res = pthread_attr_init(&attr);
+ if (nxt_slow_path(res != 0)) {
+ nxt_unit_alert(NULL, "thread attr init failed: %s (%d)",
+ strerror(res), res);
+
+ return NXT_UNIT_ERROR;
+ }
+
+ res = pthread_attr_setstacksize(&attr, c->thread_stack_size);
+ if (nxt_slow_path(res != 0)) {
+ nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)",
+ strerror(res), res);
+
+ return NXT_UNIT_ERROR;
+ }
+
+ nxt_java_thread_attr = &attr;
+ }
+
+ nxt_java_threads = nxt_unit_malloc(NULL,
+ sizeof(pthread_t) * (c->threads - 1));
+ if (nxt_slow_path(nxt_java_threads == NULL)) {
+ nxt_unit_alert(NULL, "Failed to allocate thread id array");
+
+ return NXT_UNIT_ERROR;
+ }
+
+ memset(nxt_java_threads, 0, sizeof(pthread_t) * (c->threads - 1));
+
+ return NXT_UNIT_OK;
+}
+
+
+static void
+nxt_java_join_threads(nxt_unit_ctx_t *ctx, nxt_java_app_conf_t *c)
+{
+ int res;
+ uint32_t i;
+
+ if (nxt_java_threads == NULL) {
+ return;
+ }
+
+ for (i = 0; i < c->threads - 1; i++) {
+ if ((uintptr_t) nxt_java_threads[i] == 0) {
+ continue;
+ }
+
+ res = pthread_join(nxt_java_threads[i], NULL);
+
+ if (nxt_fast_path(res == 0)) {
+ nxt_unit_debug(ctx, "thread #%d joined", (int) i);
+
+ } else {
+ nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)",
+ (int) i, strerror(res), res);
+ }
+ }
+
+ nxt_unit_free(ctx, nxt_java_threads);
+}
+
+
diff --git a/src/nxt_lib.c b/src/nxt_lib.c
index 1634a2b8..aba07dda 100644
--- a/src/nxt_lib.c
+++ b/src/nxt_lib.c
@@ -91,9 +91,12 @@ nxt_lib_start(const char *app, char **argv, char ***envp)
#elif (NXT_HPUX)
n = mpctl(MPC_GETNUMSPUS, NULL, NULL);
+#else
+ n = 0;
+
#endif
- nxt_debug(&nxt_main_task, "ncpu: %ui", n);
+ nxt_debug(&nxt_main_task, "ncpu: %d", n);
if (n > 1) {
nxt_ncpu = n;
diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c
index d2edab1d..0cde435b 100644
--- a/src/nxt_main_process.c
+++ b/src/nxt_main_process.c
@@ -197,6 +197,24 @@ static nxt_conf_map_t nxt_python_app_conf[] = {
NXT_CONF_MAP_CSTRZ,
offsetof(nxt_common_app_conf_t, u.python.callable),
},
+
+ {
+ nxt_string("protocol"),
+ NXT_CONF_MAP_STR,
+ offsetof(nxt_common_app_conf_t, u.python.protocol),
+ },
+
+ {
+ nxt_string("threads"),
+ NXT_CONF_MAP_INT32,
+ offsetof(nxt_common_app_conf_t, u.python.threads),
+ },
+
+ {
+ nxt_string("thread_stack_size"),
+ NXT_CONF_MAP_INT32,
+ offsetof(nxt_common_app_conf_t, u.python.thread_stack_size),
+ },
};
@@ -221,6 +239,18 @@ static nxt_conf_map_t nxt_perl_app_conf[] = {
NXT_CONF_MAP_CSTRZ,
offsetof(nxt_common_app_conf_t, u.perl.script),
},
+
+ {
+ nxt_string("threads"),
+ NXT_CONF_MAP_INT32,
+ offsetof(nxt_common_app_conf_t, u.perl.threads),
+ },
+
+ {
+ nxt_string("thread_stack_size"),
+ NXT_CONF_MAP_INT32,
+ offsetof(nxt_common_app_conf_t, u.perl.thread_stack_size),
+ },
};
@@ -230,6 +260,11 @@ static nxt_conf_map_t nxt_ruby_app_conf[] = {
NXT_CONF_MAP_STR,
offsetof(nxt_common_app_conf_t, u.ruby.script),
},
+ {
+ nxt_string("threads"),
+ NXT_CONF_MAP_INT32,
+ offsetof(nxt_common_app_conf_t, u.ruby.threads),
+ },
};
@@ -254,6 +289,16 @@ static nxt_conf_map_t nxt_java_app_conf[] = {
NXT_CONF_MAP_CSTRZ,
offsetof(nxt_common_app_conf_t, u.java.unit_jars),
},
+ {
+ nxt_string("threads"),
+ NXT_CONF_MAP_INT32,
+ offsetof(nxt_common_app_conf_t, u.java.threads),
+ },
+ {
+ nxt_string("thread_stack_size"),
+ NXT_CONF_MAP_INT32,
+ offsetof(nxt_common_app_conf_t, u.java.thread_stack_size),
+ },
};
@@ -1163,9 +1208,14 @@ static nxt_conf_map_t nxt_app_lang_mounts_map[] = {
offsetof(nxt_fs_mount_t, dst),
},
{
- nxt_string("fstype"),
+ nxt_string("name"),
NXT_CONF_MAP_CSTRZ,
- offsetof(nxt_fs_mount_t, fstype),
+ offsetof(nxt_fs_mount_t, name),
+ },
+ {
+ nxt_string("type"),
+ NXT_CONF_MAP_INT,
+ offsetof(nxt_fs_mount_t, type),
},
{
nxt_string("flags"),
@@ -1297,6 +1347,7 @@ nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
}
mnt->builtin = 1;
+ mnt->deps = 1;
ret = nxt_conf_map_object(rt->mem_pool, value,
nxt_app_lang_mounts_map,
diff --git a/src/nxt_pcre.c b/src/nxt_pcre.c
new file mode 100644
index 00000000..737fc7cf
--- /dev/null
+++ b/src/nxt_pcre.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) Axel Duch
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+#include <nxt_regex.h>
+#include <pcre.h>
+
+
+struct nxt_regex_s {
+ pcre *code;
+ pcre_extra *extra;
+ nxt_str_t pattern;
+};
+
+struct nxt_regex_match_s {
+ int ovecsize;
+ int ovec[];
+};
+
+
+static void *nxt_pcre_malloc(size_t size);
+static void nxt_pcre_free(void *p);
+
+static nxt_mp_t *nxt_pcre_mp;
+
+
+nxt_regex_t *
+nxt_regex_compile(nxt_mp_t *mp, nxt_str_t *source, nxt_regex_err_t *err)
+{
+ int erroffset;
+ char *pattern;
+ void *saved_malloc, *saved_free;
+ nxt_regex_t *re;
+
+ err->offset = source->length;
+
+ re = nxt_mp_get(mp, sizeof(nxt_regex_t) + source->length + 1);
+ if (nxt_slow_path(re == NULL)) {
+ err->msg = "memory allocation failed";
+ return NULL;
+ }
+
+ pattern = nxt_pointer_to(re, sizeof(nxt_regex_t));
+
+ nxt_memcpy(pattern, source->start, source->length);
+ pattern[source->length] = '\0';
+
+ re->pattern.length = source->length;
+ re->pattern.start = (u_char *) pattern;
+
+ saved_malloc = pcre_malloc;
+ saved_free = pcre_free;
+
+ pcre_malloc = nxt_pcre_malloc;
+ pcre_free = nxt_pcre_free;
+ nxt_pcre_mp = mp;
+
+ re->code = pcre_compile(pattern, 0, &err->msg, &erroffset, NULL);
+ if (nxt_fast_path(re->code != NULL)) {
+#if 0
+ re->extra = pcre_study(re->code, PCRE_STUDY_JIT_COMPILE, &err->msg);
+ if (nxt_slow_path(re->extra == NULL && err->msg != NULL)) {
+ nxt_log_warn(thr->log, "pcre_study(%V) failed: %s", source, err->msg);
+ }
+#else
+ re->extra = NULL;
+#endif
+
+ } else {
+ err->offset = erroffset;
+ re = NULL;
+ }
+
+ pcre_malloc = saved_malloc;
+ pcre_free = saved_free;
+
+ return re;
+}
+
+
+static void*
+nxt_pcre_malloc(size_t size)
+{
+ if (nxt_slow_path(nxt_pcre_mp == NULL)) {
+ nxt_thread_log_alert("pcre_malloc(%uz) called without memory pool",
+ size);
+ return NULL;
+ }
+
+ nxt_thread_log_debug("pcre_malloc(%uz), pool %p", size, nxt_pcre_mp);
+
+ return nxt_mp_get(nxt_pcre_mp, size);
+}
+
+
+static void
+nxt_pcre_free(void *p)
+{
+}
+
+
+nxt_regex_match_t *
+nxt_regex_match_create(nxt_mp_t *mp, size_t size)
+{
+ nxt_regex_match_t *match;
+
+ match = nxt_mp_get(mp, sizeof(nxt_regex_match_t) + sizeof(int) * size);
+ if (nxt_fast_path(match != NULL)) {
+ match->ovecsize = size;
+ }
+
+ return match;
+}
+
+
+nxt_int_t
+nxt_regex_match(nxt_regex_t *re, u_char *subject, size_t length,
+ nxt_regex_match_t *match)
+{
+ int ret;
+
+ ret = pcre_exec(re->code, re->extra, (const char *) subject, length, 0, 0,
+ match->ovec, match->ovecsize);
+ if (nxt_slow_path(ret < PCRE_ERROR_NOMATCH)) {
+ nxt_thread_log_error(NXT_LOG_ERR,
+ "pcre_exec() failed: %d on \"%*s\" using \"%V\"",
+ ret, length, subject, &re->pattern);
+
+ return NXT_ERROR;
+ }
+
+ return (ret != PCRE_ERROR_NOMATCH);
+}
diff --git a/src/nxt_pcre2.c b/src/nxt_pcre2.c
new file mode 100644
index 00000000..22c4d2d4
--- /dev/null
+++ b/src/nxt_pcre2.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) Axel Duch
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+#include <nxt_regex.h>
+
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
+
+
+static void *nxt_pcre2_malloc(PCRE2_SIZE size, void *memory_data);
+static void nxt_pcre2_free(void *p, void *memory_data);
+
+
+struct nxt_regex_s {
+ pcre2_code *code;
+ nxt_str_t pattern;
+};
+
+
+nxt_regex_t *
+nxt_regex_compile(nxt_mp_t *mp, nxt_str_t *source, nxt_regex_err_t *err)
+{
+ int errcode;
+ nxt_int_t ret;
+ PCRE2_SIZE erroffset;
+ nxt_regex_t *re;
+ pcre2_general_context *general_ctx;
+ pcre2_compile_context *compile_ctx;
+
+ static const u_char alloc_error[] = "memory allocation failed";
+
+ general_ctx = pcre2_general_context_create(nxt_pcre2_malloc,
+ nxt_pcre2_free, mp);
+ if (nxt_slow_path(general_ctx == NULL)) {
+ goto alloc_fail;
+ }
+
+ compile_ctx = pcre2_compile_context_create(general_ctx);
+ if (nxt_slow_path(compile_ctx == NULL)) {
+ goto alloc_fail;
+ }
+
+ re = nxt_mp_get(mp, sizeof(nxt_regex_t));
+ if (nxt_slow_path(re == NULL)) {
+ goto alloc_fail;
+ }
+
+ if (nxt_slow_path(nxt_str_dup(mp, &re->pattern, source) == NULL)) {
+ goto alloc_fail;
+ }
+
+ re->code = pcre2_compile((PCRE2_SPTR) source->start, source->length, 0,
+ &errcode, &erroffset, compile_ctx);
+ if (nxt_slow_path(re->code == NULL)) {
+ err->offset = erroffset;
+
+ ret = pcre2_get_error_message(errcode, (PCRE2_UCHAR *) err->msg,
+ ERR_BUF_SIZE);
+ if (ret < 0) {
+ (void) nxt_sprintf(err->msg, err->msg + ERR_BUF_SIZE,
+ "compilation failed with unknown "
+ "error code: %d%Z", errcode);
+ }
+
+ return NULL;
+ }
+
+#if 0
+ errcode = pcre2_jit_compile(re, PCRE2_JIT_COMPLETE);
+ if (nxt_slow_path(errcode != 0 && errcode != PCRE2_ERROR_JIT_BADOPTION)) {
+ ret = pcre2_get_error_message(errcode, (PCRE2_UCHAR *) err->msg,
+ ERR_BUF_SIZE);
+ if (ret < 0) {
+ (void) nxt_sprintf(err->msg, err->msg + ERR_BUF_SIZE,
+ "JIT compilation failed with unknown "
+ "error code: %d%Z", errcode);
+ }
+
+ return NULL;
+ }
+#endif
+
+ return re;
+
+alloc_fail:
+
+ err->offset = source->length;
+ nxt_memcpy(err->msg, alloc_error, sizeof(alloc_error));
+
+ return NULL;
+}
+
+
+static void *
+nxt_pcre2_malloc(PCRE2_SIZE size, void *mp)
+{
+ return nxt_mp_get(mp, size);
+}
+
+
+static void
+nxt_pcre2_free(void *p, void *mp)
+{
+}
+
+
+nxt_regex_match_t *
+nxt_regex_match_create(nxt_mp_t *mp, size_t size)
+{
+ nxt_regex_match_t *match;
+ pcre2_general_context *ctx;
+
+ ctx = pcre2_general_context_create(nxt_pcre2_malloc, nxt_pcre2_free, mp);
+ if (nxt_slow_path(ctx == NULL)) {
+ nxt_thread_log_alert("pcre2_general_context_create() failed");
+ return NULL;
+ }
+
+ match = pcre2_match_data_create(size, ctx);
+ if (nxt_slow_path(match == NULL)) {
+ nxt_thread_log_alert("pcre2_match_data_create(%uz) failed", size);
+ return NULL;
+ }
+
+ return match;
+}
+
+
+nxt_int_t
+nxt_regex_match(nxt_regex_t *re, u_char *subject, size_t length,
+ nxt_regex_match_t *match)
+{
+ nxt_int_t ret;
+ PCRE2_UCHAR errptr[ERR_BUF_SIZE];
+
+ ret = pcre2_match(re->code, (PCRE2_SPTR) subject, length, 0, 0, match,
+ NULL);
+
+ if (nxt_slow_path(ret < PCRE2_ERROR_NOMATCH)) {
+
+ 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,
+ &re->pattern);
+
+ } else {
+ nxt_thread_log_error(NXT_LOG_ERR,
+ "pcre2_match() failed: %s (%d) on \"%*s\" "
+ "using \"%V\"", errptr, ret, length, subject,
+ &re->pattern);
+ }
+
+ return NXT_ERROR;
+ }
+
+ return (ret != PCRE2_ERROR_NOMATCH);
+}
diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c
index 234ceef8..d2fbdd27 100644
--- a/src/nxt_php_sapi.c
+++ b/src/nxt_php_sapi.c
@@ -8,6 +8,7 @@
#include "SAPI.h"
#include "php_main.h"
#include "php_variables.h"
+#include "ext/standard/php_standard.h"
#include <nxt_main.h>
#include <nxt_router.h>
@@ -137,16 +138,38 @@ static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC);
#endif
+#ifdef NXT_PHP7
+#if PHP_VERSION_ID < 70200
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0,
+ _IS_BOOL, NULL, 0)
+#else
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0,
+ _IS_BOOL, 0)
+#endif
+#else /* PHP5 */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, 0)
+#endif
+ZEND_END_ARG_INFO()
+
+ZEND_FUNCTION(fastcgi_finish_request);
+
PHP_MINIT_FUNCTION(nxt_php_ext);
ZEND_NAMED_FUNCTION(nxt_php_chdir);
+
+static const zend_function_entry nxt_php_ext_functions[] = {
+ ZEND_FE(fastcgi_finish_request, arginfo_fastcgi_finish_request)
+ ZEND_FE_END
+};
+
+
zif_handler nxt_php_chdir_handler;
static zend_module_entry nxt_php_unit_module = {
STANDARD_MODULE_HEADER,
"unit",
- NULL, /* function table */
+ nxt_php_ext_functions, /* function table */
PHP_MINIT(nxt_php_ext), /* initialization */
NULL, /* shutdown */
NULL, /* request initialization */
@@ -186,6 +209,55 @@ ZEND_NAMED_FUNCTION(nxt_php_chdir)
}
+PHP_FUNCTION(fastcgi_finish_request)
+{
+ nxt_php_run_ctx_t *ctx;
+
+ if (nxt_slow_path(zend_parse_parameters_none() == FAILURE)) {
+#ifdef NXT_PHP8
+ RETURN_THROWS();
+#else
+ return;
+#endif
+ }
+
+ ctx = SG(server_context);
+
+ if (nxt_slow_path(ctx->req == NULL)) {
+ RETURN_FALSE;
+ }
+
+#ifdef NXT_PHP7
+ php_output_end_all();
+ php_header();
+#else
+#ifdef PHP_OUTPUT_NEWAPI
+ php_output_end_all(TSRMLS_C);
+#else
+ php_end_ob_buffers(1 TSRMLS_CC);
+#endif
+
+ php_header(TSRMLS_C);
+#endif
+
+ nxt_unit_request_done(ctx->req, NXT_UNIT_OK);
+ ctx->req = NULL;
+
+ PG(connection_status) = PHP_CONNECTION_ABORTED;
+#ifdef NXT_PHP7
+ php_output_set_status(PHP_OUTPUT_DISABLED);
+#else
+#ifdef PHP_OUTPUT_NEWAPI
+ php_output_set_status(PHP_OUTPUT_DISABLED TSRMLS_CC);
+#else
+ php_output_set_status(0 TSRMLS_CC);
+#endif
+#endif
+
+ RETURN_TRUE;
+}
+
+
static sapi_module_struct nxt_php_sapi_module =
{
(char *) "cli-server",
@@ -934,6 +1006,9 @@ nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
static void
nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
{
+#if (PHP_VERSION_ID < 50600)
+ void *read_post;
+#endif
nxt_unit_field_t *f;
zend_file_handle file_handle;
@@ -990,9 +1065,23 @@ nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
php_execute_script(&file_handle TSRMLS_CC);
+ /* Prevention of consuming possible unread request body. */
+#if (PHP_VERSION_ID < 50600)
+ read_post = sapi_module.read_post;
+ sapi_module.read_post = NULL;
+#else
+ SG(post_read) = 1;
+#endif
+
php_request_shutdown(NULL);
- nxt_unit_request_done(ctx->req, NXT_UNIT_OK);
+ if (ctx->req != NULL) {
+ nxt_unit_request_done(ctx->req, NXT_UNIT_OK);
+ }
+
+#if (PHP_VERSION_ID < 50600)
+ sapi_module.read_post = read_post;
+#endif
}
diff --git a/src/nxt_port.h b/src/nxt_port.h
index 3ac8c735..5ece3bfa 100644
--- a/src/nxt_port.h
+++ b/src/nxt_port.h
@@ -26,6 +26,7 @@ struct nxt_port_handlers_s {
nxt_port_handler_t change_file;
nxt_port_handler_t new_port;
nxt_port_handler_t get_port;
+ nxt_port_handler_t port_ack;
nxt_port_handler_t mmap;
nxt_port_handler_t get_mmap;
@@ -84,6 +85,7 @@ typedef enum {
_NXT_PORT_MSG_CHANGE_FILE = nxt_port_handler_idx(change_file),
_NXT_PORT_MSG_NEW_PORT = nxt_port_handler_idx(new_port),
_NXT_PORT_MSG_GET_PORT = nxt_port_handler_idx(get_port),
+ _NXT_PORT_MSG_PORT_ACK = nxt_port_handler_idx(port_ack),
_NXT_PORT_MSG_MMAP = nxt_port_handler_idx(mmap),
_NXT_PORT_MSG_GET_MMAP = nxt_port_handler_idx(get_mmap),
@@ -120,6 +122,7 @@ typedef enum {
NXT_PORT_MSG_CHANGE_FILE = nxt_msg_last(_NXT_PORT_MSG_CHANGE_FILE),
NXT_PORT_MSG_NEW_PORT = nxt_msg_last(_NXT_PORT_MSG_NEW_PORT),
NXT_PORT_MSG_GET_PORT = nxt_msg_last(_NXT_PORT_MSG_GET_PORT),
+ NXT_PORT_MSG_PORT_ACK = nxt_msg_last(_NXT_PORT_MSG_PORT_ACK),
NXT_PORT_MSG_MMAP = nxt_msg_last(_NXT_PORT_MSG_MMAP)
| NXT_PORT_MSG_SYNC,
NXT_PORT_MSG_GET_MMAP = nxt_msg_last(_NXT_PORT_MSG_GET_MMAP),
diff --git a/src/nxt_port_memory.c b/src/nxt_port_memory.c
index 1e01629e..ae9f079c 100644
--- a/src/nxt_port_memory.c
+++ b/src/nxt_port_memory.c
@@ -17,6 +17,10 @@
#include <nxt_port_memory_int.h>
+static void nxt_port_broadcast_shm_ack(nxt_task_t *task, nxt_port_t *port,
+ void *data);
+
+
nxt_inline void
nxt_port_mmap_handler_use(nxt_port_mmap_handler_t *mmap_handler, int i)
{
@@ -112,7 +116,6 @@ nxt_port_mmap_buf_completion(nxt_task_t *task, void *obj, void *data)
u_char *p;
nxt_mp_t *mp;
nxt_buf_t *b, *next;
- nxt_port_t *port;
nxt_process_t *process;
nxt_chunk_id_t c;
nxt_port_mmap_header_t *hdr;
@@ -171,14 +174,7 @@ complete_buf:
{
process = nxt_runtime_process_find(task->thread->runtime, hdr->src_pid);
- if (process != NULL && !nxt_queue_is_empty(&process->ports)) {
- port = nxt_process_port_first(process);
-
- if (port->type == NXT_PROCESS_APP) {
- (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_SHM_ACK,
- -1, 0, 0, NULL);
- }
- }
+ nxt_process_broadcast_shm_ack(task, process);
}
release_buf:
@@ -976,3 +972,35 @@ nxt_port_mmap_get_method(nxt_task_t *task, nxt_port_t *port, nxt_buf_t *b)
return m;
}
+
+
+void
+nxt_process_broadcast_shm_ack(nxt_task_t *task, nxt_process_t *process)
+{
+ nxt_port_t *port;
+
+ if (nxt_slow_path(process == NULL || nxt_queue_is_empty(&process->ports)))
+ {
+ return;
+ }
+
+ port = nxt_process_port_first(process);
+
+ if (port->type == NXT_PROCESS_APP) {
+ nxt_port_post(task, port, nxt_port_broadcast_shm_ack, process);
+ }
+}
+
+
+static void
+nxt_port_broadcast_shm_ack(nxt_task_t *task, nxt_port_t *port, void *data)
+{
+ nxt_process_t *process;
+
+ process = data;
+
+ nxt_queue_each(port, &process->ports, nxt_port_t, link) {
+ (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_SHM_ACK,
+ -1, 0, 0, NULL);
+ } nxt_queue_loop;
+}
diff --git a/src/nxt_port_memory.h b/src/nxt_port_memory.h
index 8e71af3d..a2cdf5dd 100644
--- a/src/nxt_port_memory.h
+++ b/src/nxt_port_memory.h
@@ -71,4 +71,6 @@ nxt_port_mmap_get_method(nxt_task_t *task, nxt_port_t *port, nxt_buf_t *b);
nxt_int_t nxt_shm_open(nxt_task_t *task, size_t size);
+void nxt_process_broadcast_shm_ack(nxt_task_t *task, nxt_process_t *process);
+
#endif /* _NXT_PORT_MEMORY_H_INCLUDED_ */
diff --git a/src/nxt_process.c b/src/nxt_process.c
index 9be7974f..87419313 100644
--- a/src/nxt_process.c
+++ b/src/nxt_process.c
@@ -248,8 +248,6 @@ nxt_process_setup(nxt_task_t *task, nxt_process_t *process)
port = nxt_process_port_first(process);
- nxt_port_write_close(port);
-
nxt_port_enable(task, port, init->port_handlers);
ret = init->setup(task, process);
@@ -272,6 +270,9 @@ nxt_process_setup(nxt_task_t *task, nxt_process_t *process)
}
ret = init->start(task, &process->data);
+
+ nxt_port_write_close(port);
+
break;
default:
diff --git a/src/nxt_process.h b/src/nxt_process.h
index d9b4dff1..7afb8803 100644
--- a/src/nxt_process.h
+++ b/src/nxt_process.h
@@ -74,7 +74,9 @@ typedef struct {
typedef struct {
- uint8_t language_deps; /* 1-byte */
+ uint8_t language_deps; /* 1-bit */
+ uint8_t tmpfs; /* 1-bit */
+ uint8_t procfs; /* 1-bit */
} nxt_process_automount_t;
diff --git a/src/nxt_regex.h b/src/nxt_regex.h
new file mode 100644
index 00000000..a24c50f8
--- /dev/null
+++ b/src/nxt_regex.h
@@ -0,0 +1,41 @@
+
+/*
+ * Copyright (C) Axel Duch
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_REGEX_H_INCLUDED_
+#define _NXT_REGEX_H_INCLUDED_
+
+#if (NXT_HAVE_REGEX)
+
+typedef struct nxt_regex_s nxt_regex_t;
+
+ #if (NXT_HAVE_PCRE2)
+typedef void nxt_regex_match_t;
+#else
+typedef struct nxt_regex_match_s nxt_regex_match_t;
+#endif
+
+typedef struct {
+ size_t offset;
+
+#if (NXT_HAVE_PCRE2)
+#define ERR_BUF_SIZE 256
+ u_char msg[ERR_BUF_SIZE];
+#else
+ const char *msg;
+#endif
+} nxt_regex_err_t;
+
+
+NXT_EXPORT void nxt_regex_init(void);
+NXT_EXPORT nxt_regex_t *nxt_regex_compile(nxt_mp_t *mp, nxt_str_t *source,
+ nxt_regex_err_t *err);
+NXT_EXPORT nxt_regex_match_t *nxt_regex_match_create(nxt_mp_t *mp, size_t size);
+NXT_EXPORT nxt_int_t nxt_regex_match(nxt_regex_t *re, u_char *subject,
+ size_t length, nxt_regex_match_t *match);
+
+#endif /* NXT_HAVE_REGEX */
+
+#endif /* _NXT_REGEX_H_INCLUDED_ */
diff --git a/src/nxt_router.c b/src/nxt_router.c
index a3218047..9dd5c30e 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -688,6 +688,8 @@ nxt_router_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
port->app = app;
port->main_app_port = main_app_port;
+
+ nxt_port_socket_write(task, port, NXT_PORT_MSG_PORT_ACK, -1, 0, 0, NULL);
}
@@ -1260,6 +1262,12 @@ static nxt_conf_map_t nxt_router_http_conf[] = {
NXT_CONF_MAP_STR,
offsetof(nxt_socket_conf_t, body_temp_path),
},
+
+ {
+ nxt_string("discard_unsafe_fields"),
+ NXT_CONF_MAP_INT8,
+ offsetof(nxt_socket_conf_t, discard_unsafe_fields),
+ },
};
@@ -1647,6 +1655,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
skcf->header_buffer_size = 2048;
skcf->large_header_buffer_size = 8192;
skcf->large_header_buffers = 4;
+ skcf->discard_unsafe_fields = 1;
skcf->body_buffer_size = 16 * 1024;
skcf->max_body_size = 8 * 1024 * 1024;
skcf->proxy_header_buffer_size = 64 * 1024;
@@ -3722,6 +3731,7 @@ static void
nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
void *data)
{
+ size_t b_size, count;
nxt_int_t ret;
nxt_app_t *app;
nxt_buf_t *b, *next;
@@ -3787,15 +3797,20 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
nxt_http_request_send_body(task, r, NULL);
} else {
- size_t b_size = nxt_buf_mem_used_size(&b->mem);
+ b_size = nxt_buf_is_mem(b) ? nxt_buf_mem_used_size(&b->mem) : 0;
- if (nxt_slow_path(b_size < sizeof(*resp))) {
+ if (nxt_slow_path(b_size < sizeof(nxt_unit_response_t))) {
+ nxt_alert(task, "response buffer too small: %z", b_size);
goto fail;
}
resp = (void *) b->mem.pos;
- if (nxt_slow_path(b_size < sizeof(*resp)
- + resp->fields_count * sizeof(nxt_unit_field_t))) {
+ count = (b_size - sizeof(nxt_unit_response_t))
+ / sizeof(nxt_unit_field_t);
+
+ if (nxt_slow_path(count < resp->fields_count)) {
+ nxt_alert(task, "response buffer too small for fields count: %D",
+ resp->fields_count);
goto fail;
}
@@ -3904,6 +3919,7 @@ nxt_router_req_headers_ack_handler(nxt_task_t *task,
{
int res;
nxt_app_t *app;
+ nxt_buf_t *b;
nxt_bool_t start_process, unlinked;
nxt_port_t *app_port, *main_app_port, *idle_port;
nxt_queue_link_t *idle_lnk;
@@ -4001,16 +4017,25 @@ nxt_router_req_headers_ack_handler(nxt_task_t *task,
req_rpc_data->app_port = app_port;
- if (req_rpc_data->msg_info.body_fd != -1) {
+ b = req_rpc_data->msg_info.buf;
+
+ if (b != NULL) {
+ /* First buffer is already sent. Start from second. */
+ b = b->next;
+ }
+
+ if (req_rpc_data->msg_info.body_fd != -1 || b != NULL) {
nxt_debug(task, "stream #%uD: send body fd %d", req_rpc_data->stream,
req_rpc_data->msg_info.body_fd);
- lseek(req_rpc_data->msg_info.body_fd, 0, SEEK_SET);
+ if (req_rpc_data->msg_info.body_fd != -1) {
+ lseek(req_rpc_data->msg_info.body_fd, 0, SEEK_SET);
+ }
res = nxt_port_socket_write(task, app_port, NXT_PORT_MSG_REQ_BODY,
req_rpc_data->msg_info.body_fd,
req_rpc_data->stream,
- task->thread->engine->port->id, NULL);
+ task->thread->engine->port->id, b);
if (nxt_slow_path(res != NXT_OK)) {
nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
@@ -4667,6 +4692,25 @@ nxt_router_free_app(nxt_task_t *task, void *obj, void *data)
nxt_port_use(task, port, -1);
}
+ nxt_thread_mutex_lock(&app->mutex);
+
+ for ( ;; ) {
+ port = nxt_port_hash_retrieve(&app->port_hash);
+ if (port == NULL) {
+ break;
+ }
+
+ app->port_hash_count--;
+
+ port->app = NULL;
+
+ nxt_port_close(task, port);
+
+ nxt_port_use(task, port, -1);
+ }
+
+ nxt_thread_mutex_unlock(&app->mutex);
+
nxt_assert(app->processes == 0);
nxt_assert(app->active_requests == 0);
nxt_assert(app->port_hash_count == 0);
@@ -5364,8 +5408,7 @@ nxt_router_oosm_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
nxt_thread_mutex_unlock(&process->incoming.mutex);
if (ack) {
- (void) nxt_port_socket_write(task, msg->port, NXT_PORT_MSG_SHM_ACK,
- -1, 0, 0, NULL);
+ nxt_process_broadcast_shm_ack(task, process);
}
}
diff --git a/src/nxt_router.h b/src/nxt_router.h
index 512f1810..5804840f 100644
--- a/src/nxt_router.h
+++ b/src/nxt_router.h
@@ -191,6 +191,8 @@ typedef struct {
nxt_str_t body_temp_path;
+ uint8_t discard_unsafe_fields; /* 1 bit */
+
#if (NXT_TLS)
nxt_tls_conf_t *tls;
#endif
diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c
index 44970b34..d9d986da 100644
--- a/src/nxt_runtime.c
+++ b/src/nxt_runtime.c
@@ -10,6 +10,7 @@
#include <nxt_port.h>
#include <nxt_main_process.h>
#include <nxt_router.h>
+#include <nxt_regex.h>
static nxt_int_t nxt_runtime_inherited_listen_sockets(nxt_task_t *task,
@@ -702,9 +703,6 @@ nxt_runtime_thread_pool_destroy(nxt_task_t *task, nxt_runtime_t *rt,
static void
nxt_runtime_thread_pool_init(void)
{
-#if (NXT_REGEX)
- nxt_regex_init(0);
-#endif
}
diff --git a/src/nxt_unit.c b/src/nxt_unit.c
index f75d61bc..097f50d6 100644
--- a/src/nxt_unit.c
+++ b/src/nxt_unit.c
@@ -54,11 +54,13 @@ static int nxt_unit_read_env(nxt_unit_port_t *ready_port,
int *log_fd, uint32_t *stream, uint32_t *shm_limit);
static int nxt_unit_ready(nxt_unit_ctx_t *ctx, int ready_fd, uint32_t stream,
int queue_fd);
-static int nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf);
+static int nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf,
+ nxt_unit_request_info_t **preq);
static int nxt_unit_process_new_port(nxt_unit_ctx_t *ctx,
nxt_unit_recv_msg_t *recv_msg);
+static int nxt_unit_ctx_ready(nxt_unit_ctx_t *ctx);
static int nxt_unit_process_req_headers(nxt_unit_ctx_t *ctx,
- nxt_unit_recv_msg_t *recv_msg);
+ nxt_unit_recv_msg_t *recv_msg, nxt_unit_request_info_t **preq);
static int nxt_unit_process_req_body(nxt_unit_ctx_t *ctx,
nxt_unit_recv_msg_t *recv_msg);
static int nxt_unit_request_check_response_port(nxt_unit_request_info_t *req,
@@ -106,6 +108,8 @@ static int nxt_unit_get_outgoing_buf(nxt_unit_ctx_t *ctx,
uint32_t min_size, nxt_unit_mmap_buf_t *mmap_buf, char *local_buf);
static int nxt_unit_incoming_mmap(nxt_unit_ctx_t *ctx, pid_t pid, int fd);
+static void nxt_unit_awake_ctx(nxt_unit_ctx_t *ctx,
+ nxt_unit_ctx_impl_t *ctx_impl);
static void nxt_unit_mmaps_init(nxt_unit_mmaps_t *mmaps);
nxt_inline void nxt_unit_process_use(nxt_unit_process_t *process);
nxt_inline void nxt_unit_process_release(nxt_unit_process_t *process);
@@ -144,6 +148,8 @@ nxt_inline void nxt_unit_port_use(nxt_unit_port_t *port);
nxt_inline void nxt_unit_port_release(nxt_unit_port_t *port);
static nxt_unit_port_t *nxt_unit_add_port(nxt_unit_ctx_t *ctx,
nxt_unit_port_t *port, void *queue);
+static void nxt_unit_process_awaiting_req(nxt_unit_ctx_t *ctx,
+ nxt_queue_t *awaiting_req);
static void nxt_unit_remove_port(nxt_unit_impl_t *lib,
nxt_unit_port_id_t *port_id);
static nxt_unit_port_t *nxt_unit_remove_port_unsafe(nxt_unit_impl_t *lib,
@@ -305,6 +311,9 @@ struct nxt_unit_ctx_impl_s {
/* of nxt_unit_read_buf_t */
nxt_queue_t free_rbuf;
+ int online;
+ int ready;
+
nxt_unit_mmap_buf_t ctx_buf[2];
nxt_unit_read_buf_t ctx_read_buf;
@@ -314,6 +323,7 @@ struct nxt_unit_ctx_impl_s {
struct nxt_unit_mmap_s {
nxt_port_mmap_header_t *hdr;
+ pthread_t src_thread;
/* of nxt_unit_read_buf_t */
nxt_queue_t awaiting_rbuf;
@@ -353,7 +363,6 @@ struct nxt_unit_impl_s {
pid_t pid;
int log_fd;
- int online;
nxt_unit_ctx_impl_t main_ctx;
};
@@ -560,7 +569,6 @@ nxt_unit_create(nxt_unit_init_t *init)
lib->ports.slot = NULL;
lib->log_fd = STDERR_FILENO;
- lib->online = 1;
nxt_queue_init(&lib->contexts);
@@ -614,10 +622,16 @@ nxt_unit_ctx_init(nxt_unit_impl_t *lib, nxt_unit_ctx_impl_t *ctx_impl,
nxt_unit_lib_use(lib);
+ pthread_mutex_lock(&lib->mutex);
+
nxt_queue_insert_tail(&lib->contexts, &ctx_impl->link);
+ pthread_mutex_unlock(&lib->mutex);
+
ctx_impl->use_count = 1;
ctx_impl->wait_items = 0;
+ ctx_impl->online = 1;
+ ctx_impl->ready = 0;
nxt_queue_init(&ctx_impl->free_req);
nxt_queue_init(&ctx_impl->free_ws);
@@ -769,7 +783,7 @@ nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port,
uint32_t *shm_limit)
{
int rc;
- int ready_fd, router_fd, read_fd;
+ int ready_fd, router_fd, read_in_fd, read_out_fd;
char *unit_init, *version_end;
long version_length;
int64_t ready_pid, router_pid, read_pid;
@@ -801,15 +815,15 @@ nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port,
"%"PRIu32";"
"%"PRId64",%"PRIu32",%d;"
"%"PRId64",%"PRIu32",%d;"
- "%"PRId64",%"PRIu32",%d;"
+ "%"PRId64",%"PRIu32",%d,%d;"
"%d,%"PRIu32,
&ready_stream,
&ready_pid, &ready_id, &ready_fd,
&router_pid, &router_id, &router_fd,
- &read_pid, &read_id, &read_fd,
+ &read_pid, &read_id, &read_in_fd, &read_out_fd,
log_fd, shm_limit);
- if (nxt_slow_path(rc != 12)) {
+ if (nxt_slow_path(rc != 13)) {
nxt_unit_alert(NULL, "failed to scan variables: %d", rc);
return NXT_UNIT_ERROR;
@@ -829,8 +843,8 @@ nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *router_port,
nxt_unit_port_id_init(&read_port->id, (pid_t) read_pid, read_id);
- read_port->in_fd = read_fd;
- read_port->out_fd = -1;
+ read_port->in_fd = read_in_fd;
+ read_port->out_fd = read_out_fd;
read_port->data = NULL;
*stream = ready_stream;
@@ -891,7 +905,8 @@ nxt_unit_ready(nxt_unit_ctx_t *ctx, int ready_fd, uint32_t stream, int queue_fd)
static int
-nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf)
+nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf,
+ nxt_unit_request_info_t **preq)
{
int rc;
pid_t pid;
@@ -979,6 +994,10 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf)
switch (port_msg->type) {
+ case _NXT_PORT_MSG_RPC_READY:
+ rc = NXT_UNIT_OK;
+ break;
+
case _NXT_PORT_MSG_QUIT:
nxt_unit_debug(ctx, "#%"PRIu32": quit", port_msg->stream);
@@ -990,6 +1009,10 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf)
rc = nxt_unit_process_new_port(ctx, &recv_msg);
break;
+ case _NXT_PORT_MSG_PORT_ACK:
+ rc = nxt_unit_ctx_ready(ctx);
+ break;
+
case _NXT_PORT_MSG_CHANGE_FILE:
nxt_unit_debug(ctx, "#%"PRIu32": change_file: fd %d",
port_msg->stream, recv_msg.fd[0]);
@@ -1019,7 +1042,7 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf)
break;
case _NXT_PORT_MSG_REQ_HEADERS:
- rc = nxt_unit_process_req_headers(ctx, &recv_msg);
+ rc = nxt_unit_process_req_headers(ctx, &recv_msg, preq);
break;
case _NXT_PORT_MSG_REQ_BODY:
@@ -1055,7 +1078,7 @@ nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf)
break;
default:
- nxt_unit_debug(ctx, "#%"PRIu32": ignore message type: %d",
+ nxt_unit_alert(ctx, "#%"PRIu32": ignore message type: %d",
port_msg->stream, (int) port_msg->type);
rc = NXT_UNIT_ERROR;
@@ -1118,7 +1141,7 @@ nxt_unit_process_new_port(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
- if (new_port_msg->id == (nxt_port_id_t) -1) {
+ if (new_port_msg->id == NXT_UNIT_SHARED_PORT_ID) {
nxt_unit_port_id_init(&new_port.id, lib->pid, new_port_msg->id);
new_port.in_fd = recv_msg->fd[0];
@@ -1160,11 +1183,31 @@ nxt_unit_process_new_port(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
return NXT_UNIT_ERROR;
}
- if (new_port_msg->id == (nxt_port_id_t) -1) {
+ if (new_port_msg->id == NXT_UNIT_SHARED_PORT_ID) {
lib->shared_port = port;
- } else {
- nxt_unit_port_release(port);
+ return nxt_unit_ctx_ready(ctx);
+ }
+
+ nxt_unit_port_release(port);
+
+ return NXT_UNIT_OK;
+}
+
+
+static int
+nxt_unit_ctx_ready(nxt_unit_ctx_t *ctx)
+{
+ nxt_unit_impl_t *lib;
+ nxt_unit_ctx_impl_t *ctx_impl;
+
+ lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+ ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
+
+ ctx_impl->ready = 1;
+
+ if (lib->callbacks.ready_handler) {
+ return lib->callbacks.ready_handler(ctx);
}
return NXT_UNIT_OK;
@@ -1172,7 +1215,8 @@ nxt_unit_process_new_port(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
static int
-nxt_unit_process_req_headers(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
+nxt_unit_process_req_headers(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg,
+ nxt_unit_request_info_t **preq)
{
int res;
nxt_unit_impl_t *lib;
@@ -1288,7 +1332,12 @@ nxt_unit_process_req_headers(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
}
}
- lib->callbacks.request_handler(req);
+ if (preq == NULL) {
+ lib->callbacks.request_handler(req);
+
+ } else {
+ *preq = req;
+ }
}
return NXT_UNIT_OK;
@@ -1318,8 +1367,14 @@ nxt_unit_process_req_body(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
if (recv_msg->incoming_buf != NULL) {
b = nxt_container_of(req->content_buf, nxt_unit_mmap_buf_t, buf);
+ while (b->next != NULL) {
+ b = b->next;
+ }
+
/* "Move" incoming buffer list to req_impl. */
- nxt_unit_mmap_buf_insert_tail(&b->next, recv_msg->incoming_buf);
+ b->next = recv_msg->incoming_buf;
+ b->next->prev = &b->next;
+
recv_msg->incoming_buf = NULL;
}
@@ -1588,8 +1643,6 @@ nxt_unit_process_websocket(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg)
}
if (recv_msg->last) {
- req_impl->websocket = 0;
-
if (cb->close_handler) {
nxt_unit_req_debug(req, "close_handler");
@@ -1682,8 +1735,6 @@ nxt_unit_request_info_release(nxt_unit_request_info_t *req)
nxt_unit_request_hash_find(req->ctx, req_impl->stream, 1);
}
- req_impl->websocket = 0;
-
while (req_impl->outgoing_buf != NULL) {
nxt_unit_mmap_buf_free(req_impl->outgoing_buf);
}
@@ -1704,6 +1755,8 @@ nxt_unit_request_info_release(nxt_unit_request_info_t *req)
req->response_port = NULL;
}
+ req_impl->state = NXT_UNIT_RS_RELEASED;
+
pthread_mutex_lock(&ctx_impl->mutex);
nxt_queue_remove(&req_impl->link);
@@ -1711,8 +1764,6 @@ nxt_unit_request_info_release(nxt_unit_request_info_t *req)
nxt_queue_insert_tail(&ctx_impl->free_req, &req_impl->link);
pthread_mutex_unlock(&ctx_impl->mutex);
-
- req_impl->state = NXT_UNIT_RS_RELEASED;
}
@@ -2132,7 +2183,8 @@ nxt_unit_response_add_field(nxt_unit_request_info_t *req,
resp = req->response;
if (nxt_slow_path(resp->fields_count >= req->response_max_fields)) {
- nxt_unit_req_warn(req, "add_field: too many response fields");
+ nxt_unit_req_warn(req, "add_field: too many response fields (%d)",
+ (int) resp->fields_count);
return NXT_UNIT_ERROR;
}
@@ -2309,6 +2361,8 @@ nxt_unit_response_buf_alloc(nxt_unit_request_info_t *req, uint32_t size)
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
nxt_unit_mmap_buf_release(mmap_buf);
+ nxt_unit_req_alert(req, "response_buf_alloc: failed to get out buf");
+
return NULL;
}
@@ -2949,8 +3003,6 @@ nxt_unit_request_read(nxt_unit_request_info_t *req, void *dst, size_t size)
buf_res = nxt_unit_buf_read(&req->content_buf, &req->content_length,
dst, size);
- nxt_unit_req_debug(req, "read: %d", (int) buf_res);
-
if (buf_res < (ssize_t) size && req->content_fd != -1) {
res = read(req->content_fd, dst, size);
if (nxt_slow_path(res < 0)) {
@@ -3389,7 +3441,10 @@ retry:
for (mm = lib->outgoing.elts; mm < mm_end; mm++) {
hdr = mm->hdr;
- if (hdr->sent_over != 0xFFFFu && hdr->sent_over != port->id.id) {
+ if (hdr->sent_over != 0xFFFFu
+ && (hdr->sent_over != port->id.id
+ || mm->src_thread != pthread_self()))
+ {
continue;
}
@@ -3657,6 +3712,7 @@ nxt_unit_new_mmap(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, int n)
hdr->src_pid = lib->pid;
hdr->dst_pid = port->id.pid;
hdr->sent_over = port->id.id;
+ mm->src_thread = pthread_self();
/* Mark first n chunk(s) as busy */
for (i = 0; i < n; i++) {
@@ -3975,6 +4031,8 @@ nxt_unit_incoming_mmap(nxt_unit_ctx_t *ctx, pid_t pid, int fd)
nxt_atomic_fetch_add(&ctx_impl->wait_items, -1);
+ nxt_unit_awake_ctx(ctx, ctx_impl);
+
} nxt_queue_loop;
return rc;
@@ -3982,6 +4040,32 @@ nxt_unit_incoming_mmap(nxt_unit_ctx_t *ctx, pid_t pid, int fd)
static void
+nxt_unit_awake_ctx(nxt_unit_ctx_t *ctx, nxt_unit_ctx_impl_t *ctx_impl)
+{
+ nxt_port_msg_t msg;
+
+ if (nxt_fast_path(ctx == &ctx_impl->ctx)) {
+ return;
+ }
+
+ if (nxt_slow_path(ctx_impl->read_port == NULL
+ || ctx_impl->read_port->out_fd == -1))
+ {
+ nxt_unit_alert(ctx, "target context read_port is NULL or not writable");
+
+ return;
+ }
+
+ memset(&msg, 0, sizeof(nxt_port_msg_t));
+
+ msg.type = _NXT_PORT_MSG_RPC_READY;
+
+ (void) nxt_unit_port_send(ctx, ctx_impl->read_port,
+ &msg, sizeof(msg), NULL, 0);
+}
+
+
+static void
nxt_unit_mmaps_init(nxt_unit_mmaps_t *mmaps)
{
pthread_mutex_init(&mmaps->mutex, NULL);
@@ -4403,18 +4487,20 @@ nxt_unit_process_pop_first(nxt_unit_impl_t *lib)
int
nxt_unit_run(nxt_unit_ctx_t *ctx)
{
- int rc;
- nxt_unit_impl_t *lib;
+ int rc;
+ nxt_unit_ctx_impl_t *ctx_impl;
nxt_unit_ctx_use(ctx);
- lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+ ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
+
rc = NXT_UNIT_OK;
- while (nxt_fast_path(lib->online)) {
+ while (nxt_fast_path(ctx_impl->online)) {
rc = nxt_unit_run_once_impl(ctx);
if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
+ nxt_unit_quit(ctx);
break;
}
}
@@ -4458,7 +4544,7 @@ nxt_unit_run_once_impl(nxt_unit_ctx_t *ctx)
return rc;
}
- rc = nxt_unit_process_msg(ctx, rbuf);
+ rc = nxt_unit_process_msg(ctx, rbuf, NULL);
if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
return NXT_UNIT_ERROR;
}
@@ -4483,17 +4569,17 @@ nxt_unit_read_buf(nxt_unit_ctx_t *ctx, nxt_unit_read_buf_t *rbuf)
nxt_unit_port_impl_t *port_impl;
struct pollfd fds[2];
- lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
- if (ctx_impl->wait_items > 0 || lib->shared_port == NULL) {
-
+ if (ctx_impl->wait_items > 0 || ctx_impl->ready == 0) {
return nxt_unit_ctx_port_recv(ctx, ctx_impl->read_port, rbuf);
}
port_impl = nxt_container_of(ctx_impl->read_port, nxt_unit_port_impl_t,
port);
+ lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+
retry:
if (port_impl->from_socket == 0) {
@@ -4585,8 +4671,6 @@ nxt_unit_process_pending_rbuf(nxt_unit_ctx_t *ctx)
nxt_unit_ctx_impl_t *ctx_impl;
nxt_unit_read_buf_t *rbuf;
- nxt_queue_init(&pending_rbuf);
-
ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
pthread_mutex_lock(&ctx_impl->mutex);
@@ -4597,6 +4681,8 @@ nxt_unit_process_pending_rbuf(nxt_unit_ctx_t *ctx)
return NXT_UNIT_OK;
}
+ nxt_queue_init(&pending_rbuf);
+
nxt_queue_add(&pending_rbuf, &ctx_impl->pending_rbuf);
nxt_queue_init(&ctx_impl->pending_rbuf);
@@ -4607,7 +4693,7 @@ nxt_unit_process_pending_rbuf(nxt_unit_ctx_t *ctx)
nxt_queue_each(rbuf, &pending_rbuf, nxt_unit_read_buf_t, link) {
if (nxt_fast_path(rc != NXT_UNIT_ERROR)) {
- rc = nxt_unit_process_msg(&ctx_impl->ctx, rbuf);
+ rc = nxt_unit_process_msg(&ctx_impl->ctx, rbuf, NULL);
} else {
nxt_unit_read_buf_release(ctx, rbuf);
@@ -4629,8 +4715,6 @@ nxt_unit_process_ready_req(nxt_unit_ctx_t *ctx)
nxt_unit_request_info_t *req;
nxt_unit_request_info_impl_t *req_impl;
- nxt_queue_init(&ready_req);
-
ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
pthread_mutex_lock(&ctx_impl->mutex);
@@ -4641,6 +4725,8 @@ nxt_unit_process_ready_req(nxt_unit_ctx_t *ctx)
return;
}
+ nxt_queue_init(&ready_req);
+
nxt_queue_add(&ready_req, &ctx_impl->ready_req);
nxt_queue_init(&ctx_impl->ready_req);
@@ -4691,18 +4777,16 @@ int
nxt_unit_run_ctx(nxt_unit_ctx_t *ctx)
{
int rc;
- nxt_unit_impl_t *lib;
nxt_unit_read_buf_t *rbuf;
nxt_unit_ctx_impl_t *ctx_impl;
nxt_unit_ctx_use(ctx);
- lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
rc = NXT_UNIT_OK;
- while (nxt_fast_path(lib->online)) {
+ while (nxt_fast_path(ctx_impl->online)) {
rbuf = nxt_unit_read_buf_get(ctx);
if (nxt_slow_path(rbuf == NULL)) {
rc = NXT_UNIT_ERROR;
@@ -4716,7 +4800,7 @@ nxt_unit_run_ctx(nxt_unit_ctx_t *ctx)
goto retry;
}
- rc = nxt_unit_process_msg(ctx, rbuf);
+ rc = nxt_unit_process_msg(ctx, rbuf, NULL);
if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
break;
}
@@ -4797,13 +4881,16 @@ nxt_unit_run_shared(nxt_unit_ctx_t *ctx)
int rc;
nxt_unit_impl_t *lib;
nxt_unit_read_buf_t *rbuf;
+ nxt_unit_ctx_impl_t *ctx_impl;
nxt_unit_ctx_use(ctx);
lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+ ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
+
rc = NXT_UNIT_OK;
- while (nxt_fast_path(lib->online)) {
+ while (nxt_fast_path(ctx_impl->online)) {
rbuf = nxt_unit_read_buf_get(ctx);
if (nxt_slow_path(rbuf == NULL)) {
rc = NXT_UNIT_ERROR;
@@ -4822,22 +4909,67 @@ nxt_unit_run_shared(nxt_unit_ctx_t *ctx)
break;
}
- rc = nxt_unit_process_msg(ctx, rbuf);
+ rc = nxt_unit_process_msg(ctx, rbuf, NULL);
if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
break;
}
+ }
- rc = nxt_unit_process_pending_rbuf(ctx);
- if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
- break;
- }
+ nxt_unit_ctx_release(ctx);
- nxt_unit_process_ready_req(ctx);
+ return rc;
+}
+
+
+nxt_unit_request_info_t *
+nxt_unit_dequeue_request(nxt_unit_ctx_t *ctx)
+{
+ int rc;
+ nxt_unit_impl_t *lib;
+ nxt_unit_read_buf_t *rbuf;
+ nxt_unit_ctx_impl_t *ctx_impl;
+ nxt_unit_request_info_t *req;
+
+ nxt_unit_ctx_use(ctx);
+
+ lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+ ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
+
+ req = NULL;
+
+ if (nxt_slow_path(!ctx_impl->online)) {
+ goto done;
}
+ rbuf = nxt_unit_read_buf_get(ctx);
+ if (nxt_slow_path(rbuf == NULL)) {
+ goto done;
+ }
+
+ rc = nxt_unit_app_queue_recv(lib->shared_port, rbuf);
+ if (rc != NXT_UNIT_OK) {
+ nxt_unit_read_buf_release(ctx, rbuf);
+ goto done;
+ }
+
+ (void) nxt_unit_process_msg(ctx, rbuf, &req);
+
+done:
+
nxt_unit_ctx_release(ctx);
- return rc;
+ return req;
+}
+
+
+int
+nxt_unit_is_main_ctx(nxt_unit_ctx_t *ctx)
+{
+ nxt_unit_impl_t *lib;
+
+ lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+
+ return (ctx == &lib->main_ctx.ctx);
}
@@ -4862,6 +4994,7 @@ nxt_unit_process_port_msg_impl(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
int rc;
nxt_unit_impl_t *lib;
nxt_unit_read_buf_t *rbuf;
+ nxt_unit_ctx_impl_t *ctx_impl;
rbuf = nxt_unit_read_buf_get(ctx);
if (nxt_slow_path(rbuf == NULL)) {
@@ -4869,6 +5002,7 @@ nxt_unit_process_port_msg_impl(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
}
lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+ ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
retry:
@@ -4884,7 +5018,7 @@ retry:
return rc;
}
- rc = nxt_unit_process_msg(ctx, rbuf);
+ rc = nxt_unit_process_msg(ctx, rbuf, NULL);
if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
return NXT_UNIT_ERROR;
}
@@ -4896,12 +5030,12 @@ retry:
nxt_unit_process_ready_req(ctx);
- rbuf = nxt_unit_read_buf_get(ctx);
- if (nxt_slow_path(rbuf == NULL)) {
- return NXT_UNIT_ERROR;
- }
+ if (ctx_impl->online) {
+ rbuf = nxt_unit_read_buf_get(ctx);
+ if (nxt_slow_path(rbuf == NULL)) {
+ return NXT_UNIT_ERROR;
+ }
- if (lib->online) {
goto retry;
}
@@ -4945,14 +5079,14 @@ nxt_unit_ctx_alloc(nxt_unit_ctx_t *ctx, void *data)
queue_fd = -1;
- port = nxt_unit_create_port(ctx);
+ port = nxt_unit_create_port(&new_ctx->ctx);
if (nxt_slow_path(port == NULL)) {
goto fail;
}
new_ctx->read_port = port;
- queue_fd = nxt_unit_shm_open(ctx, sizeof(nxt_port_queue_t));
+ queue_fd = nxt_unit_shm_open(&new_ctx->ctx, sizeof(nxt_port_queue_t));
if (nxt_slow_path(queue_fd == -1)) {
goto fail;
}
@@ -4971,7 +5105,7 @@ nxt_unit_ctx_alloc(nxt_unit_ctx_t *ctx, void *data)
port_impl = nxt_container_of(port, nxt_unit_port_impl_t, port);
port_impl->queue = mem;
- rc = nxt_unit_send_port(ctx, lib->router_port, port, queue_fd);
+ rc = nxt_unit_send_port(&new_ctx->ctx, lib->router_port, port, queue_fd);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
goto fail;
}
@@ -4997,6 +5131,7 @@ nxt_unit_ctx_free(nxt_unit_ctx_impl_t *ctx_impl)
{
nxt_unit_impl_t *lib;
nxt_unit_mmap_buf_t *mmap_buf;
+ nxt_unit_read_buf_t *rbuf;
nxt_unit_request_info_impl_t *req_impl;
nxt_unit_websocket_frame_impl_t *ws_impl;
@@ -5034,10 +5169,21 @@ nxt_unit_ctx_free(nxt_unit_ctx_impl_t *ctx_impl)
} nxt_queue_loop;
+ nxt_queue_each(rbuf, &ctx_impl->free_rbuf, nxt_unit_read_buf_t, link)
+ {
+ if (rbuf != &ctx_impl->ctx_read_buf) {
+ nxt_unit_free(&ctx_impl->ctx, rbuf);
+ }
+ } nxt_queue_loop;
+
pthread_mutex_destroy(&ctx_impl->mutex);
+ pthread_mutex_lock(&lib->mutex);
+
nxt_queue_remove(&ctx_impl->link);
+ pthread_mutex_unlock(&lib->mutex);
+
if (nxt_fast_path(ctx_impl->read_port != NULL)) {
nxt_unit_remove_port(lib, &ctx_impl->read_port->id);
nxt_unit_port_release(ctx_impl->read_port);
@@ -5224,7 +5370,7 @@ nxt_inline void nxt_unit_port_release(nxt_unit_port_t *port)
}
if (port_impl->queue != NULL) {
- munmap(port_impl->queue, (port->id.id == (nxt_port_id_t) -1)
+ munmap(port_impl->queue, (port->id.id == NXT_UNIT_SHARED_PORT_ID)
? sizeof(nxt_app_queue_t)
: sizeof(nxt_port_queue_t));
}
@@ -5237,14 +5383,12 @@ nxt_inline void nxt_unit_port_release(nxt_unit_port_t *port)
static nxt_unit_port_t *
nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, void *queue)
{
- int rc;
- nxt_queue_t awaiting_req;
- nxt_unit_impl_t *lib;
- nxt_unit_port_t *old_port;
- nxt_unit_process_t *process;
- nxt_unit_ctx_impl_t *ctx_impl;
- nxt_unit_port_impl_t *new_port, *old_port_impl;
- nxt_unit_request_info_impl_t *req_impl;
+ int rc, ready;
+ nxt_queue_t awaiting_req;
+ nxt_unit_impl_t *lib;
+ nxt_unit_port_t *old_port;
+ nxt_unit_process_t *process;
+ nxt_unit_port_impl_t *new_port, *old_port_impl;
lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
@@ -5293,44 +5437,45 @@ nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, void *queue)
old_port_impl->queue = queue;
}
- if (!nxt_queue_is_empty(&old_port_impl->awaiting_req)) {
- nxt_queue_add(&awaiting_req, &old_port_impl->awaiting_req);
- nxt_queue_init(&old_port_impl->awaiting_req);
- }
-
- old_port_impl->ready = (port->in_fd != -1 || port->out_fd != -1);
+ ready = (port->in_fd != -1 || port->out_fd != -1);
- pthread_mutex_unlock(&lib->mutex);
+ /*
+ * Port can be market as 'ready' only after callbacks.add_port() call.
+ * Otherwise, request may try to use the port before callback.
+ */
+ if (lib->callbacks.add_port == NULL && ready) {
+ old_port_impl->ready = ready;
- if (lib->callbacks.add_port != NULL
- && (port->in_fd != -1 || port->out_fd != -1))
- {
- lib->callbacks.add_port(ctx, old_port);
+ if (!nxt_queue_is_empty(&old_port_impl->awaiting_req)) {
+ nxt_queue_add(&awaiting_req, &old_port_impl->awaiting_req);
+ nxt_queue_init(&old_port_impl->awaiting_req);
+ }
}
- nxt_queue_each(req_impl, &awaiting_req,
- nxt_unit_request_info_impl_t, port_wait_link)
- {
- nxt_queue_remove(&req_impl->port_wait_link);
+ pthread_mutex_unlock(&lib->mutex);
- ctx_impl = nxt_container_of(req_impl->req.ctx, nxt_unit_ctx_impl_t,
- ctx);
+ if (lib->callbacks.add_port != NULL && ready) {
+ lib->callbacks.add_port(ctx, old_port);
- pthread_mutex_lock(&ctx_impl->mutex);
+ pthread_mutex_lock(&lib->mutex);
- nxt_queue_insert_tail(&ctx_impl->ready_req,
- &req_impl->port_wait_link);
+ old_port_impl->ready = ready;
- pthread_mutex_unlock(&ctx_impl->mutex);
+ if (!nxt_queue_is_empty(&old_port_impl->awaiting_req)) {
+ nxt_queue_add(&awaiting_req, &old_port_impl->awaiting_req);
+ nxt_queue_init(&old_port_impl->awaiting_req);
+ }
- nxt_atomic_fetch_add(&ctx_impl->wait_items, -1);
+ pthread_mutex_unlock(&lib->mutex);
+ }
- } nxt_queue_loop;
+ nxt_unit_process_awaiting_req(ctx, &awaiting_req);
return old_port;
}
new_port = NULL;
+ ready = 0;
nxt_unit_debug(ctx, "add_port: port{%d,%d} in_fd %d out_fd %d queue %p",
port->id.pid, port->id.id,
@@ -5341,7 +5486,9 @@ nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, void *queue)
goto unlock;
}
- if (port->id.id >= process->next_port_id) {
+ if (port->id.id != NXT_UNIT_SHARED_PORT_ID
+ && port->id.id >= process->next_port_id)
+ {
process->next_port_id = port->id.id + 1;
}
@@ -5371,13 +5518,21 @@ nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port, void *queue)
new_port->use_count = 2;
new_port->process = process;
- new_port->ready = (port->in_fd != -1 || port->out_fd != -1);
new_port->queue = queue;
new_port->from_socket = 0;
new_port->socket_rbuf = NULL;
nxt_queue_init(&new_port->awaiting_req);
+ ready = (port->in_fd != -1 || port->out_fd != -1);
+
+ if (lib->callbacks.add_port == NULL) {
+ new_port->ready = ready;
+
+ } else {
+ new_port->ready = 0;
+ }
+
process = NULL;
unlock:
@@ -5388,14 +5543,55 @@ unlock:
nxt_unit_process_release(process);
}
- if (lib->callbacks.add_port != NULL
- && new_port != NULL
- && (port->in_fd != -1 || port->out_fd != -1))
- {
+ if (lib->callbacks.add_port != NULL && new_port != NULL && ready) {
lib->callbacks.add_port(ctx, &new_port->port);
+
+ nxt_queue_init(&awaiting_req);
+
+ pthread_mutex_lock(&lib->mutex);
+
+ new_port->ready = 1;
+
+ if (!nxt_queue_is_empty(&new_port->awaiting_req)) {
+ nxt_queue_add(&awaiting_req, &new_port->awaiting_req);
+ nxt_queue_init(&new_port->awaiting_req);
+ }
+
+ pthread_mutex_unlock(&lib->mutex);
+
+ nxt_unit_process_awaiting_req(ctx, &awaiting_req);
}
- return &new_port->port;
+ return (new_port == NULL) ? NULL : &new_port->port;
+}
+
+
+static void
+nxt_unit_process_awaiting_req(nxt_unit_ctx_t *ctx, nxt_queue_t *awaiting_req)
+{
+ nxt_unit_ctx_impl_t *ctx_impl;
+ nxt_unit_request_info_impl_t *req_impl;
+
+ nxt_queue_each(req_impl, awaiting_req,
+ nxt_unit_request_info_impl_t, port_wait_link)
+ {
+ nxt_queue_remove(&req_impl->port_wait_link);
+
+ ctx_impl = nxt_container_of(req_impl->req.ctx, nxt_unit_ctx_impl_t,
+ ctx);
+
+ pthread_mutex_lock(&ctx_impl->mutex);
+
+ nxt_queue_insert_tail(&ctx_impl->ready_req,
+ &req_impl->port_wait_link);
+
+ pthread_mutex_unlock(&ctx_impl->mutex);
+
+ nxt_atomic_fetch_add(&ctx_impl->wait_items, -1);
+
+ nxt_unit_awake_ctx(ctx, ctx_impl);
+
+ } nxt_queue_loop;
}
@@ -5509,17 +5705,72 @@ nxt_unit_remove_process(nxt_unit_impl_t *lib, nxt_unit_process_t *process)
static void
nxt_unit_quit(nxt_unit_ctx_t *ctx)
{
- nxt_unit_impl_t *lib;
+ nxt_port_msg_t msg;
+ nxt_unit_impl_t *lib;
+ nxt_unit_ctx_impl_t *ctx_impl;
+ nxt_unit_callbacks_t *cb;
+ nxt_unit_request_info_t *req;
+ nxt_unit_request_info_impl_t *req_impl;
lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit);
+ ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx);
+
+ if (!ctx_impl->online) {
+ return;
+ }
+
+ ctx_impl->online = 0;
+
+ cb = &lib->callbacks;
+
+ if (cb->quit != NULL) {
+ cb->quit(ctx);
+ }
+
+ nxt_queue_each(req_impl, &ctx_impl->active_req,
+ nxt_unit_request_info_impl_t, link)
+ {
+ req = &req_impl->req;
+
+ nxt_unit_req_warn(req, "active request on ctx quit");
- if (lib->online) {
- lib->online = 0;
+ if (cb->close_handler) {
+ nxt_unit_req_debug(req, "close_handler");
+
+ cb->close_handler(req);
- if (lib->callbacks.quit != NULL) {
- lib->callbacks.quit(ctx);
+ } else {
+ nxt_unit_request_done(req, NXT_UNIT_ERROR);
}
+
+ } nxt_queue_loop;
+
+ if (ctx != &lib->main_ctx.ctx) {
+ return;
}
+
+ memset(&msg, 0, sizeof(nxt_port_msg_t));
+
+ msg.pid = lib->pid;
+ msg.type = _NXT_PORT_MSG_QUIT;
+
+ pthread_mutex_lock(&lib->mutex);
+
+ nxt_queue_each(ctx_impl, &lib->contexts, nxt_unit_ctx_impl_t, link) {
+
+ if (ctx == &ctx_impl->ctx
+ || ctx_impl->read_port == NULL
+ || ctx_impl->read_port->out_fd == -1)
+ {
+ continue;
+ }
+
+ (void) nxt_unit_port_send(ctx, ctx_impl->read_port,
+ &msg, sizeof(msg), NULL, 0);
+
+ } nxt_queue_loop;
+
+ pthread_mutex_unlock(&lib->mutex);
}
@@ -6388,7 +6639,9 @@ nxt_unit_malloc(nxt_unit_ctx_t *ctx, size_t size)
p = malloc(size);
if (nxt_fast_path(p != NULL)) {
+#if (NXT_DEBUG_ALLOC)
nxt_unit_debug(ctx, "malloc(%d): %p", (int) size, p);
+#endif
} else {
nxt_unit_alert(ctx, "malloc(%d) failed: %s (%d)",
@@ -6402,7 +6655,9 @@ nxt_unit_malloc(nxt_unit_ctx_t *ctx, size_t size)
void
nxt_unit_free(nxt_unit_ctx_t *ctx, void *p)
{
+#if (NXT_DEBUG_ALLOC)
nxt_unit_debug(ctx, "free(%p)", p);
+#endif
free(p);
}
diff --git a/src/nxt_unit.h b/src/nxt_unit.h
index e90f0781..1e1a8dbe 100644
--- a/src/nxt_unit.h
+++ b/src/nxt_unit.h
@@ -12,6 +12,7 @@
#include <sys/uio.h>
#include <string.h>
+#include "nxt_auto_config.h"
#include "nxt_version.h"
#include "nxt_unit_typedefs.h"
@@ -34,6 +35,8 @@ enum {
#define NXT_UNIT_INIT_ENV "NXT_UNIT_INIT"
+#define NXT_UNIT_SHARED_PORT_ID ((uint16_t) 0xFFFFu)
+
/*
* Mostly opaque structure with library state.
*
@@ -152,6 +155,8 @@ struct nxt_unit_callbacks_s {
/* Receive data on port id. Optional. */
ssize_t (*port_recv)(nxt_unit_ctx_t *, nxt_unit_port_t *port,
void *buf, size_t buf_size, void *oob, size_t oob_size);
+
+ int (*ready_handler)(nxt_unit_ctx_t *);
};
@@ -208,6 +213,10 @@ int nxt_unit_run_ctx(nxt_unit_ctx_t *ctx);
int nxt_unit_run_shared(nxt_unit_ctx_t *ctx);
+nxt_unit_request_info_t *nxt_unit_dequeue_request(nxt_unit_ctx_t *ctx);
+
+int nxt_unit_is_main_ctx(nxt_unit_ctx_t *ctx);
+
/*
* Receive and process one message, invoke configured callbacks.
*
diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c
index 16079a38..5df1465d 100644
--- a/src/perl/nxt_perl_psgi.c
+++ b/src/perl/nxt_perl_psgi.c
@@ -18,14 +18,14 @@
typedef struct {
PerlInterpreter *my_perl;
- nxt_unit_request_info_t *req;
-} nxt_perl_psgi_input_t;
-
-
-typedef struct {
- PerlInterpreter *my_perl;
+ nxt_perl_psgi_io_arg_t arg_input;
+ nxt_perl_psgi_io_arg_t arg_error;
SV *app;
-} nxt_perl_psgi_module_t;
+ CV *cb;
+ nxt_unit_request_info_t *req;
+ pthread_t thread;
+ nxt_unit_ctx_t *ctx;
+} nxt_perl_psgi_ctx_t;
static long nxt_perl_psgi_io_input_read(PerlInterpreter *my_perl,
@@ -62,11 +62,11 @@ static nxt_int_t nxt_perl_psgi_io_input_init(PerlInterpreter *my_perl,
static nxt_int_t nxt_perl_psgi_io_error_init(PerlInterpreter *my_perl,
nxt_perl_psgi_io_arg_t *arg);
-static PerlInterpreter *nxt_perl_psgi_interpreter_init(nxt_task_t *task,
- char *script, SV **app);
+static int nxt_perl_psgi_ctx_init(const char *script,
+ nxt_perl_psgi_ctx_t *pctx);
static SV *nxt_perl_psgi_env_create(PerlInterpreter *my_perl,
- nxt_unit_request_info_t *req, nxt_perl_psgi_input_t *input);
+ nxt_unit_request_info_t *req);
nxt_inline int nxt_perl_psgi_add_sptr(PerlInterpreter *my_perl, HV *hash_env,
const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len);
nxt_inline int nxt_perl_psgi_add_str(PerlInterpreter *my_perl, HV *hash_env,
@@ -75,8 +75,7 @@ nxt_inline int nxt_perl_psgi_add_value(PerlInterpreter *my_perl, HV *hash_env,
const char *name, uint32_t name_len, void *value);
-static u_char *nxt_perl_psgi_module_create(nxt_task_t *task,
- const char *script);
+static char *nxt_perl_psgi_module_create(const char *script);
static nxt_int_t nxt_perl_psgi_result_status(PerlInterpreter *my_perl,
SV *result);
@@ -96,18 +95,20 @@ static void nxt_perl_psgi_result_cb(PerlInterpreter *my_perl, SV *result,
nxt_unit_request_info_t *req);
static nxt_int_t nxt_perl_psgi_start(nxt_task_t *task,
- nxt_process_data_t *conf);
+ nxt_process_data_t *data);
static void nxt_perl_psgi_request_handler(nxt_unit_request_info_t *req);
-static void nxt_perl_psgi_atexit(void);
-
-typedef SV *(*nxt_perl_psgi_callback_f)(PerlInterpreter *my_perl,
- SV *env, nxt_task_t *task);
-
-static CV *nxt_perl_psgi_cb;
-static PerlInterpreter *nxt_perl_psgi;
-static nxt_perl_psgi_io_arg_t nxt_perl_psgi_arg_input;
-static nxt_perl_psgi_io_arg_t nxt_perl_psgi_arg_error;
-static nxt_unit_request_info_t *nxt_perl_psgi_request;
+static int nxt_perl_psgi_ready_handler(nxt_unit_ctx_t *ctx);
+static void *nxt_perl_psgi_thread_func(void *main_ctx);
+static int nxt_perl_psgi_init_threads(nxt_perl_app_conf_t *c);
+static void nxt_perl_psgi_join_threads(nxt_unit_ctx_t *ctx,
+ nxt_perl_app_conf_t *c);
+static void nxt_perl_psgi_ctx_free(nxt_perl_psgi_ctx_t *pctx);
+
+static CV *nxt_perl_psgi_write;
+static CV *nxt_perl_psgi_close;
+static CV *nxt_perl_psgi_cb;
+static pthread_attr_t *nxt_perl_psgi_thread_attr;
+static nxt_perl_psgi_ctx_t *nxt_perl_psgi_ctxs;
static uint32_t nxt_perl_psgi_compat[] = {
NXT_VERNUM, NXT_DEBUG,
@@ -129,11 +130,11 @@ static long
nxt_perl_psgi_io_input_read(PerlInterpreter *my_perl,
nxt_perl_psgi_io_arg_t *arg, void *vbuf, size_t length)
{
- nxt_perl_psgi_input_t *input;
+ nxt_perl_psgi_ctx_t *pctx;
- input = (nxt_perl_psgi_input_t *) arg->ctx;
+ pctx = arg->pctx;
- return nxt_unit_request_read(input->req, vbuf, length);
+ return nxt_unit_request_read(pctx->req, vbuf, length);
}
@@ -165,10 +166,11 @@ static long
nxt_perl_psgi_io_error_write(PerlInterpreter *my_perl,
nxt_perl_psgi_io_arg_t *arg, const void *vbuf, size_t length)
{
- nxt_perl_psgi_input_t *input;
+ nxt_perl_psgi_ctx_t *pctx;
- input = (nxt_perl_psgi_input_t *) arg->ctx;
- nxt_unit_req_error(input->req, "Perl: %s", (const char*) vbuf);
+ pctx = arg->pctx;
+
+ nxt_unit_req_error(pctx->req, "Perl: %s", (const char*) vbuf);
return (long) length;
}
@@ -216,9 +218,10 @@ XS(XS_NGINX__Unit__PSGI_exit)
XS(XS_NGINX__Unit__Sandbox_write);
XS(XS_NGINX__Unit__Sandbox_write)
{
- int rc;
- char *body;
- size_t len;
+ int rc;
+ char *body;
+ size_t len;
+ nxt_perl_psgi_ctx_t *pctx;
dXSARGS;
@@ -230,7 +233,9 @@ XS(XS_NGINX__Unit__Sandbox_write)
body = SvPV(ST(1), len);
- rc = nxt_unit_response_write(nxt_perl_psgi_request, body, len);
+ pctx = CvXSUBANY(cv).any_ptr;
+
+ rc = nxt_unit_response_write(pctx->req, body, len);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
Perl_croak(aTHX_ "Failed to write response body");
@@ -242,15 +247,11 @@ XS(XS_NGINX__Unit__Sandbox_write)
nxt_inline void
-nxt_perl_psgi_cb_request_done(nxt_int_t status)
+nxt_perl_psgi_cb_request_done(nxt_perl_psgi_ctx_t *pctx, int status)
{
- nxt_unit_request_info_t *req;
-
- req = nxt_perl_psgi_request;
-
- if (req != NULL) {
- nxt_unit_request_done(req, status);
- nxt_perl_psgi_request = NULL;
+ if (pctx->req != NULL) {
+ nxt_unit_request_done(pctx->req, status);
+ pctx->req = NULL;
}
}
@@ -262,7 +263,7 @@ XS(XS_NGINX__Unit__Sandbox_close)
ax = POPMARK;
- nxt_perl_psgi_cb_request_done(NXT_UNIT_OK);
+ nxt_perl_psgi_cb_request_done(CvXSUBANY(cv).any_ptr, NXT_UNIT_OK);
XSRETURN_NO;
}
@@ -271,14 +272,15 @@ XS(XS_NGINX__Unit__Sandbox_close)
XS(XS_NGINX__Unit__Sandbox_cb);
XS(XS_NGINX__Unit__Sandbox_cb)
{
- SV *obj;
- int rc;
- long array_len;
+ SV *obj;
+ int rc;
+ long array_len;
+ nxt_perl_psgi_ctx_t *pctx;
dXSARGS;
if (nxt_slow_path(items != 1)) {
- nxt_perl_psgi_cb_request_done(NXT_UNIT_ERROR);
+ nxt_perl_psgi_cb_request_done(CvXSUBANY(cv).any_ptr, NXT_UNIT_ERROR);
Perl_croak(aTHX_ "Wrong number of arguments");
@@ -288,7 +290,7 @@ XS(XS_NGINX__Unit__Sandbox_cb)
if (nxt_slow_path(SvOK(ST(0)) == 0 || SvROK(ST(0)) == 0
|| SvTYPE(SvRV(ST(0))) != SVt_PVAV))
{
- nxt_perl_psgi_cb_request_done(NXT_UNIT_ERROR);
+ nxt_perl_psgi_cb_request_done(CvXSUBANY(cv).any_ptr, NXT_UNIT_ERROR);
Perl_croak(aTHX_ "PSGI: An unexpected response was received "
"from Perl Application");
@@ -296,10 +298,11 @@ XS(XS_NGINX__Unit__Sandbox_cb)
XSRETURN_EMPTY;
}
- rc = nxt_perl_psgi_result_array(PERL_GET_CONTEXT, ST(0),
- nxt_perl_psgi_request);
+ pctx = CvXSUBANY(cv).any_ptr;
+
+ rc = nxt_perl_psgi_result_array(PERL_GET_CONTEXT, ST(0), pctx->req);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- nxt_perl_psgi_cb_request_done(NXT_UNIT_ERROR);
+ nxt_perl_psgi_cb_request_done(CvXSUBANY(cv).any_ptr, NXT_UNIT_ERROR);
Perl_croak(aTHX_ (char *) NULL);
@@ -316,7 +319,7 @@ XS(XS_NGINX__Unit__Sandbox_cb)
XSRETURN(1);
}
- nxt_perl_psgi_cb_request_done(NXT_UNIT_OK);
+ nxt_perl_psgi_cb_request_done(CvXSUBANY(cv).any_ptr, NXT_UNIT_OK);
XSRETURN_EMPTY;
}
@@ -335,10 +338,11 @@ nxt_perl_psgi_xs_init(pTHX)
/* DynaLoader for Perl modules who use XS */
newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
- newXS("NGINX::Unit::Sandbox::write", XS_NGINX__Unit__Sandbox_write,
- __FILE__);
- newXS("NGINX::Unit::Sandbox::close", XS_NGINX__Unit__Sandbox_close,
- __FILE__);
+ nxt_perl_psgi_write = newXS("NGINX::Unit::Sandbox::write",
+ XS_NGINX__Unit__Sandbox_write, __FILE__);
+
+ nxt_perl_psgi_close = newXS("NGINX::Unit::Sandbox::close",
+ XS_NGINX__Unit__Sandbox_close, __FILE__);
nxt_perl_psgi_cb = newXS("NGINX::Unit::Sandbox::cb",
XS_NGINX__Unit__Sandbox_cb, __FILE__);
@@ -416,10 +420,10 @@ nxt_perl_psgi_call_method(PerlInterpreter *my_perl, SV *obj, const char *method,
}
-static u_char *
-nxt_perl_psgi_module_create(nxt_task_t *task, const char *script)
+static char *
+nxt_perl_psgi_module_create(const char *script)
{
- u_char *buf, *p;
+ char *buf, *p;
size_t length;
static nxt_str_t prefix = nxt_string(
@@ -441,12 +445,11 @@ nxt_perl_psgi_module_create(nxt_task_t *task, const char *script)
length = strlen(script);
- buf = nxt_malloc(prefix.length + length + suffix.length);
-
+ buf = nxt_unit_malloc(NULL, prefix.length + length + suffix.length);
if (nxt_slow_path(buf == NULL)) {
- nxt_log_error(NXT_LOG_ERR, task->log,
- "PSGI: Failed to allocate memory "
- "for Perl script file %s", script);
+ nxt_unit_alert(NULL, "PSGI: Failed to allocate memory "
+ "for Perl script file %s", script);
+
return NULL;
}
@@ -518,30 +521,27 @@ nxt_perl_psgi_io_error_init(PerlInterpreter *my_perl,
}
-static PerlInterpreter *
-nxt_perl_psgi_interpreter_init(nxt_task_t *task, char *script, SV **app)
+static int
+nxt_perl_psgi_ctx_init(const char *script, nxt_perl_psgi_ctx_t *pctx)
{
- int status, pargc;
- char **pargv, **penv;
- u_char *run_module;
+ int status;
+ char *run_module;
PerlInterpreter *my_perl;
static char argv[] = "\0""-e\0""0";
static char *embedding[] = { &argv[0], &argv[1], &argv[4] };
- pargc = 0;
- pargv = NULL;
- penv = NULL;
-
- PERL_SYS_INIT3(&pargc, &pargv, &penv);
-
my_perl = perl_alloc();
if (nxt_slow_path(my_perl == NULL)) {
- nxt_alert(task, "PSGI: Failed to allocate memory for Perl interpreter");
- return NULL;
+ nxt_unit_alert(NULL,
+ "PSGI: Failed to allocate memory for Perl interpreter");
+
+ return NXT_UNIT_ERROR;
}
+ pctx->my_perl = my_perl;
+
run_module = NULL;
perl_construct(my_perl);
@@ -550,77 +550,86 @@ nxt_perl_psgi_interpreter_init(nxt_task_t *task, char *script, SV **app)
status = perl_parse(my_perl, nxt_perl_psgi_xs_init, 3, embedding, NULL);
if (nxt_slow_path(status != 0)) {
- nxt_alert(task, "PSGI: Failed to parse Perl Script");
+ nxt_unit_alert(NULL, "PSGI: Failed to parse Perl Script");
goto fail;
}
+ CvXSUBANY(nxt_perl_psgi_write).any_ptr = pctx;
+ CvXSUBANY(nxt_perl_psgi_close).any_ptr = pctx;
+ CvXSUBANY(nxt_perl_psgi_cb).any_ptr = pctx;
+
+ pctx->cb = nxt_perl_psgi_cb;
+
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
PL_origalen = 1;
status = perl_run(my_perl);
if (nxt_slow_path(status != 0)) {
- nxt_alert(task, "PSGI: Failed to run Perl");
+ nxt_unit_alert(NULL, "PSGI: Failed to run Perl");
goto fail;
}
sv_setsv(get_sv("0", 0), newSVpv(script, 0));
- run_module = nxt_perl_psgi_module_create(task, script);
-
+ run_module = nxt_perl_psgi_module_create(script);
if (nxt_slow_path(run_module == NULL)) {
goto fail;
}
- status = nxt_perl_psgi_io_input_init(my_perl, &nxt_perl_psgi_arg_input);
+ pctx->arg_input.pctx = pctx;
+ status = nxt_perl_psgi_io_input_init(my_perl, &pctx->arg_input);
if (nxt_slow_path(status != NXT_OK)) {
- nxt_alert(task, "PSGI: Failed to init io.psgi.input");
+ nxt_unit_alert(NULL, "PSGI: Failed to init io.psgi.input");
goto fail;
}
- status = nxt_perl_psgi_io_error_init(my_perl, &nxt_perl_psgi_arg_error);
+ pctx->arg_error.pctx = pctx;
+ status = nxt_perl_psgi_io_error_init(my_perl, &pctx->arg_error);
if (nxt_slow_path(status != NXT_OK)) {
- nxt_alert(task, "PSGI: Failed to init io.psgi.errors");
+ nxt_unit_alert(NULL, "PSGI: Failed to init io.psgi.errors");
goto fail;
}
- *app = eval_pv((const char *) run_module, FALSE);
+ pctx->app = eval_pv(run_module, FALSE);
if (SvTRUE(ERRSV)) {
- nxt_alert(task, "PSGI: Failed to parse script: %s\n%s",
- script, SvPV_nolen(ERRSV));
+ nxt_unit_alert(NULL, "PSGI: Failed to parse script: %s\n%s",
+ script, SvPV_nolen(ERRSV));
goto fail;
}
- nxt_free(run_module);
+ nxt_unit_free(NULL, run_module);
- return my_perl;
+ return NXT_UNIT_OK;
fail:
if (run_module != NULL) {
- nxt_free(run_module);
+ nxt_unit_free(NULL, run_module);
}
perl_destruct(my_perl);
perl_free(my_perl);
- PERL_SYS_TERM();
- return NULL;
+ return NXT_UNIT_ERROR;
}
static SV *
nxt_perl_psgi_env_create(PerlInterpreter *my_perl,
- nxt_unit_request_info_t *req, nxt_perl_psgi_input_t *input)
+ nxt_unit_request_info_t *req)
{
- HV *hash_env;
- AV *array_version;
- uint32_t i;
- nxt_unit_field_t *f;
- nxt_unit_request_t *r;
+ HV *hash_env;
+ AV *array_version;
+ uint32_t i;
+ nxt_unit_field_t *f;
+ nxt_unit_request_t *r;
+ nxt_perl_psgi_ctx_t *pctx;
+
+ pctx = req->ctx->data;
hash_env = newHV();
if (nxt_slow_path(hash_env == NULL)) {
@@ -664,11 +673,12 @@ nxt_perl_psgi_env_create(PerlInterpreter *my_perl,
: newSVpv("http", 4)));
RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.input"),
- SvREFCNT_inc(nxt_perl_psgi_arg_input.io)));
+ SvREFCNT_inc(pctx->arg_input.io)));
RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.errors"),
- SvREFCNT_inc(nxt_perl_psgi_arg_error.io)));
+ SvREFCNT_inc(pctx->arg_error.io)));
RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.multithread"),
- &PL_sv_no));
+ nxt_perl_psgi_ctxs != NULL
+ ? &PL_sv_yes : &PL_sv_no));
RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.multiprocess"),
&PL_sv_yes));
RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.run_once"),
@@ -1109,13 +1119,17 @@ static void
nxt_perl_psgi_result_cb(PerlInterpreter *my_perl, SV *result,
nxt_unit_request_info_t *req)
{
+ nxt_perl_psgi_ctx_t *pctx;
+
dSP;
+ pctx = req->ctx->data;
+
ENTER;
SAVETMPS;
PUSHMARK(sp);
- XPUSHs(newRV_noinc((SV*) nxt_perl_psgi_cb));
+ XPUSHs(newRV_noinc((SV*) pctx->cb));
PUTBACK;
call_sv(result, G_EVAL|G_SCALAR);
@@ -1126,7 +1140,7 @@ nxt_perl_psgi_result_cb(PerlInterpreter *my_perl, SV *result,
nxt_unit_error(NULL, "PSGI: Failed to execute result callback: \n%s",
SvPV_nolen(ERRSV));
- nxt_perl_psgi_cb_request_done(NXT_UNIT_ERROR);
+ nxt_perl_psgi_cb_request_done(pctx, NXT_UNIT_ERROR);
}
PUTBACK;
@@ -1138,90 +1152,116 @@ nxt_perl_psgi_result_cb(PerlInterpreter *my_perl, SV *result,
static nxt_int_t
nxt_perl_psgi_start(nxt_task_t *task, nxt_process_data_t *data)
{
- int rc;
- nxt_unit_ctx_t *unit_ctx;
- nxt_unit_init_t perl_init;
- PerlInterpreter *my_perl;
- nxt_common_app_conf_t *conf;
- nxt_perl_psgi_module_t module;
+ int rc, pargc;
+ char **pargv, **penv;
+ nxt_unit_ctx_t *unit_ctx;
+ nxt_unit_init_t perl_init;
+ nxt_perl_psgi_ctx_t pctx;
+ nxt_perl_app_conf_t *c;
+ nxt_common_app_conf_t *common_conf;
+
+ common_conf = data->app;
+ c = &common_conf->u.perl;
+
+ pargc = 0;
+ pargv = NULL;
+ penv = NULL;
- conf = data->app;
+ PERL_SYS_INIT3(&pargc, &pargv, &penv);
- my_perl = nxt_perl_psgi_interpreter_init(task, conf->u.perl.script,
- &module.app);
+ memset(&pctx, 0, sizeof(nxt_perl_psgi_ctx_t));
- if (nxt_slow_path(my_perl == NULL)) {
- return NXT_ERROR;
+ rc = nxt_perl_psgi_ctx_init(c->script, &pctx);
+ if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ goto fail;
}
- module.my_perl = my_perl;
- nxt_perl_psgi = my_perl;
+ rc = nxt_perl_psgi_init_threads(c);
+
+ PERL_SET_CONTEXT(pctx.my_perl);
+
+ if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ goto fail;
+ }
nxt_unit_default_init(task, &perl_init);
perl_init.callbacks.request_handler = nxt_perl_psgi_request_handler;
- perl_init.data = &module;
- perl_init.shm_limit = conf->shm_limit;
+ perl_init.callbacks.ready_handler = nxt_perl_psgi_ready_handler;
+ perl_init.data = c;
+ perl_init.ctx_data = &pctx;
+ perl_init.shm_limit = common_conf->shm_limit;
unit_ctx = nxt_unit_init(&perl_init);
if (nxt_slow_path(unit_ctx == NULL)) {
- return NXT_ERROR;
+ goto fail;
}
rc = nxt_unit_run(unit_ctx);
+ nxt_perl_psgi_join_threads(unit_ctx, c);
+
nxt_unit_done(unit_ctx);
- nxt_perl_psgi_atexit();
+ nxt_perl_psgi_ctx_free(&pctx);
+
+ PERL_SYS_TERM();
exit(rc);
return NXT_OK;
+
+fail:
+
+ nxt_perl_psgi_join_threads(NULL, c);
+
+ nxt_perl_psgi_ctx_free(&pctx);
+
+ PERL_SYS_TERM();
+
+ return NXT_ERROR;
}
static void
nxt_perl_psgi_request_handler(nxt_unit_request_info_t *req)
{
- SV *env, *result;
- nxt_int_t rc;
- PerlInterpreter *my_perl;
- nxt_perl_psgi_input_t input;
- nxt_perl_psgi_module_t *module;
-
- module = req->unit->data;
- my_perl = module->my_perl;
+ SV *env, *result;
+ nxt_int_t rc;
+ PerlInterpreter *my_perl;
+ nxt_perl_psgi_ctx_t *pctx;
- input.my_perl = my_perl;
- input.req = req;
+ pctx = req->ctx->data;
+ my_perl = pctx->my_perl;
- nxt_perl_psgi_request = req;
+ pctx->req = req;
/*
* Create environ variable for perl sub "application".
* > sub application {
* > my ($environ) = @_;
*/
- env = nxt_perl_psgi_env_create(my_perl, req, &input);
+ env = nxt_perl_psgi_env_create(my_perl, req);
if (nxt_slow_path(env == NULL)) {
nxt_unit_req_error(req,
"PSGI: Failed to create 'env' for Perl Application");
nxt_unit_request_done(req, NXT_UNIT_ERROR);
+ pctx->req = NULL;
return;
}
- nxt_perl_psgi_arg_input.ctx = &input;
- nxt_perl_psgi_arg_error.ctx = &input;
-
/* Call perl sub and get result as SV*. */
- result = nxt_perl_psgi_call_var_application(my_perl, env, module->app, req);
+ result = nxt_perl_psgi_call_var_application(my_perl, env, pctx->app,
+ req);
if (nxt_fast_path(SvOK(result) != 0 && SvROK(result) != 0)) {
if (SvTYPE(SvRV(result)) == SVt_PVAV) {
rc = nxt_perl_psgi_result_array(my_perl, result, req);
nxt_unit_request_done(req, rc);
+ pctx->req = NULL;
+
goto release;
}
@@ -1235,6 +1275,7 @@ nxt_perl_psgi_request_handler(nxt_unit_request_info_t *req)
"from Perl Application");
nxt_unit_request_done(req, NXT_UNIT_ERROR);
+ pctx->req = NULL;
release:
@@ -1243,18 +1284,181 @@ release:
}
+static int
+nxt_perl_psgi_ready_handler(nxt_unit_ctx_t *ctx)
+{
+ int res;
+ uint32_t i;
+ nxt_perl_app_conf_t *c;
+ nxt_perl_psgi_ctx_t *pctx;
+
+ /* Worker thread context. */
+ if (!nxt_unit_is_main_ctx(ctx)) {
+ return NXT_UNIT_OK;
+ }
+
+ c = ctx->unit->data;
+
+ if (c->threads <= 1) {
+ return NXT_UNIT_OK;
+ }
+
+ for (i = 0; i < c->threads - 1; i++) {
+ pctx = &nxt_perl_psgi_ctxs[i];
+
+ pctx->ctx = ctx;
+
+ res = pthread_create(&pctx->thread, nxt_perl_psgi_thread_attr,
+ nxt_perl_psgi_thread_func, pctx);
+
+ if (nxt_fast_path(res == 0)) {
+ nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1));
+
+ } else {
+ nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)",
+ (int) (i + 1), strerror(res), res);
+
+ return NXT_UNIT_ERROR;
+ }
+ }
+
+ return NXT_UNIT_OK;
+}
+
+
+static void *
+nxt_perl_psgi_thread_func(void *data)
+{
+ nxt_unit_ctx_t *ctx;
+ nxt_perl_psgi_ctx_t *pctx;
+
+ pctx = data;
+
+ nxt_unit_debug(pctx->ctx, "worker thread start");
+
+ ctx = nxt_unit_ctx_alloc(pctx->ctx, pctx);
+ if (nxt_slow_path(ctx == NULL)) {
+ return NULL;
+ }
+
+ pctx->ctx = ctx;
+
+ PERL_SET_CONTEXT(pctx->my_perl);
+
+ (void) nxt_unit_run(ctx);
+
+ nxt_unit_done(ctx);
+
+ nxt_unit_debug(NULL, "worker thread end");
+
+ return NULL;
+}
+
+
+static int
+nxt_perl_psgi_init_threads(nxt_perl_app_conf_t *c)
+{
+ int rc;
+ uint32_t i;
+ static pthread_attr_t attr;
+
+ if (c->threads <= 1) {
+ return NXT_UNIT_OK;
+ }
+
+ if (c->thread_stack_size > 0) {
+ rc = pthread_attr_init(&attr);
+ if (nxt_slow_path(rc != 0)) {
+ nxt_unit_alert(NULL, "thread attr init failed: %s (%d)",
+ strerror(rc), rc);
+
+ return NXT_UNIT_ERROR;
+ }
+
+ rc = pthread_attr_setstacksize(&attr, c->thread_stack_size);
+ if (nxt_slow_path(rc != 0)) {
+ nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)",
+ strerror(rc), rc);
+
+ return NXT_UNIT_ERROR;
+ }
+
+ nxt_perl_psgi_thread_attr = &attr;
+ }
+
+ nxt_perl_psgi_ctxs = nxt_unit_malloc(NULL, sizeof(nxt_perl_psgi_ctx_t)
+ * (c->threads - 1));
+ if (nxt_slow_path(nxt_perl_psgi_ctxs == NULL)) {
+ return NXT_UNIT_ERROR;
+ }
+
+ memset(nxt_perl_psgi_ctxs, 0, sizeof(nxt_perl_psgi_ctx_t)
+ * (c->threads - 1));
+
+ for (i = 0; i < c->threads - 1; i++) {
+ rc = nxt_perl_psgi_ctx_init(c->script, &nxt_perl_psgi_ctxs[i]);
+
+ if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ return NXT_UNIT_ERROR;
+ }
+ }
+
+ return NXT_UNIT_OK;
+}
+
+
static void
-nxt_perl_psgi_atexit(void)
+nxt_perl_psgi_join_threads(nxt_unit_ctx_t *ctx, nxt_perl_app_conf_t *c)
{
- dTHXa(nxt_perl_psgi);
+ int res;
+ uint32_t i;
+ nxt_perl_psgi_ctx_t *pctx;
- nxt_perl_psgi_layer_stream_io_destroy(aTHX_ nxt_perl_psgi_arg_input.io);
- nxt_perl_psgi_layer_stream_fp_destroy(aTHX_ nxt_perl_psgi_arg_input.fp);
+ if (nxt_perl_psgi_ctxs == NULL) {
+ return;
+ }
- nxt_perl_psgi_layer_stream_io_destroy(aTHX_ nxt_perl_psgi_arg_error.io);
- nxt_perl_psgi_layer_stream_fp_destroy(aTHX_ nxt_perl_psgi_arg_error.fp);
+ for (i = 0; i < c->threads - 1; i++) {
+ pctx = &nxt_perl_psgi_ctxs[i];
- perl_destruct(nxt_perl_psgi);
- perl_free(nxt_perl_psgi);
- PERL_SYS_TERM();
+ res = pthread_join(pctx->thread, NULL);
+
+ if (nxt_fast_path(res == 0)) {
+ nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1));
+
+ } else {
+ nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)",
+ (int) (i + 1), strerror(res), res);
+ }
+ }
+
+ for (i = 0; i < c->threads - 1; i++) {
+ nxt_perl_psgi_ctx_free(&nxt_perl_psgi_ctxs[i]);
+ }
+
+ nxt_unit_free(NULL, nxt_perl_psgi_ctxs);
+}
+
+
+static void
+nxt_perl_psgi_ctx_free(nxt_perl_psgi_ctx_t *pctx)
+{
+ PerlInterpreter *my_perl;
+
+ my_perl = pctx->my_perl;
+
+ if (nxt_slow_path(my_perl == NULL)) {
+ return;
+ }
+
+ PERL_SET_CONTEXT(my_perl);
+
+ nxt_perl_psgi_layer_stream_io_destroy(aTHX_ pctx->arg_input.io);
+ nxt_perl_psgi_layer_stream_fp_destroy(aTHX_ pctx->arg_input.fp);
+
+ nxt_perl_psgi_layer_stream_io_destroy(aTHX_ pctx->arg_error.io);
+ nxt_perl_psgi_layer_stream_fp_destroy(aTHX_ pctx->arg_error.fp);
+
+ perl_destruct(my_perl);
+ perl_free(my_perl);
}
diff --git a/src/perl/nxt_perl_psgi_layer.h b/src/perl/nxt_perl_psgi_layer.h
index 3fa349c0..af18ad0d 100644
--- a/src/perl/nxt_perl_psgi_layer.h
+++ b/src/perl/nxt_perl_psgi_layer.h
@@ -14,7 +14,7 @@
#include <perliol.h>
-typedef struct nxt_perl_psgi_io_arg nxt_perl_psgi_io_arg_t;
+typedef struct nxt_perl_psgi_io_arg_s nxt_perl_psgi_io_arg_t;
typedef long (*nxt_perl_psgi_io_read_f)(PerlInterpreter *my_perl,
nxt_perl_psgi_io_arg_t *arg, void *vbuf, size_t length);
@@ -24,7 +24,7 @@ typedef long (*nxt_perl_psgi_io_arg_f)(PerlInterpreter *my_perl,
nxt_perl_psgi_io_arg_t *arg);
-struct nxt_perl_psgi_io_arg {
+struct nxt_perl_psgi_io_arg_s {
SV *io;
PerlIO *fp;
@@ -32,7 +32,7 @@ struct nxt_perl_psgi_io_arg {
nxt_perl_psgi_io_read_f read;
nxt_perl_psgi_io_write_f write;
- void *ctx;
+ void *pctx;
};
diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c
index 01534a47..faf0c0e1 100644
--- a/src/python/nxt_python.c
+++ b/src/python/nxt_python.c
@@ -15,8 +15,20 @@
#include NXT_PYTHON_MOUNTS_H
+typedef struct {
+ pthread_t thread;
+ nxt_unit_ctx_t *ctx;
+ void *ctx_data;
+} nxt_py_thread_info_t;
+
+
static nxt_int_t nxt_python_start(nxt_task_t *task,
nxt_process_data_t *data);
+static int nxt_python_init_threads(nxt_python_app_conf_t *c);
+static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx);
+static void *nxt_python_thread_func(void *main_ctx);
+static void nxt_python_join_threads(nxt_unit_ctx_t *ctx,
+ nxt_python_app_conf_t *c);
static void nxt_python_atexit(void);
static uint32_t compat[] = {
@@ -44,14 +56,19 @@ static wchar_t *nxt_py_home;
static char *nxt_py_home;
#endif
+static pthread_attr_t *nxt_py_thread_attr;
+static nxt_py_thread_info_t *nxt_py_threads;
+static nxt_python_proto_t nxt_py_proto;
+
static nxt_int_t
nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
{
- int rc, asgi;
+ int rc;
char *nxt_py_module;
size_t len;
PyObject *obj, *pypath, *module;
+ nxt_str_t proto;
const char *callable;
nxt_unit_ctx_t *unit_ctx;
nxt_unit_init_t python_init;
@@ -66,6 +83,9 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
static const char bin_python[] = "/bin/python";
#endif
+ static const nxt_str_t wsgi = nxt_string("wsgi");
+ static const nxt_str_t asgi = nxt_string("asgi");
+
app_conf = data->app;
c = &app_conf->u.python;
@@ -124,9 +144,17 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
Py_InitializeEx(0);
+#if PY_VERSION_HEX < NXT_PYTHON_VER(3, 7)
+ if (c->threads > 1) {
+ PyEval_InitThreads();
+ }
+#endif
+
module = NULL;
obj = NULL;
+ python_init.ctx_data = NULL;
+
obj = PySys_GetObject((char *) "stderr");
if (nxt_slow_path(obj == NULL)) {
nxt_alert(task, "Python failed to get \"sys.stderr\" object");
@@ -216,35 +244,56 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
nxt_unit_default_init(task, &python_init);
+ python_init.data = c;
python_init.shm_limit = data->app->shm_limit;
+ python_init.callbacks.ready_handler = nxt_python_ready_handler;
- asgi = nxt_python_asgi_check(nxt_py_application);
+ proto = c->protocol;
- if (asgi) {
- rc = nxt_python_asgi_init(task, &python_init);
+ if (proto.length == 0) {
+ proto = nxt_python_asgi_check(nxt_py_application) ? asgi : wsgi;
+ }
+
+ if (nxt_strstr_eq(&proto, &asgi)) {
+ rc = nxt_python_asgi_init(&python_init, &nxt_py_proto);
} else {
- rc = nxt_python_wsgi_init(task, &python_init);
+ rc = nxt_python_wsgi_init(&python_init, &nxt_py_proto);
+ }
+
+ if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
+ goto fail;
+ }
+
+ rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data);
+ if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ goto fail;
}
- if (nxt_slow_path(rc == NXT_ERROR)) {
+ rc = nxt_python_init_threads(c);
+ if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
goto fail;
}
+ if (nxt_py_proto.startup != NULL) {
+ if (nxt_py_proto.startup(python_init.ctx_data) != NXT_UNIT_OK) {
+ goto fail;
+ }
+ }
+
unit_ctx = nxt_unit_init(&python_init);
if (nxt_slow_path(unit_ctx == NULL)) {
goto fail;
}
- if (asgi) {
- rc = nxt_python_asgi_run(unit_ctx);
+ rc = nxt_py_proto.run(unit_ctx);
- } else {
- rc = nxt_python_wsgi_run(unit_ctx);
- }
+ nxt_python_join_threads(unit_ctx, c);
nxt_unit_done(unit_ctx);
+ nxt_py_proto.ctx_data_free(python_init.ctx_data);
+
nxt_python_atexit();
exit(rc);
@@ -253,6 +302,12 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
fail:
+ nxt_python_join_threads(NULL, c);
+
+ if (python_init.ctx_data != NULL) {
+ nxt_py_proto.ctx_data_free(python_init.ctx_data);
+ }
+
Py_XDECREF(obj);
Py_XDECREF(module);
@@ -262,7 +317,195 @@ fail:
}
-nxt_int_t
+static int
+nxt_python_init_threads(nxt_python_app_conf_t *c)
+{
+ int res;
+ uint32_t i;
+ nxt_py_thread_info_t *ti;
+ static pthread_attr_t attr;
+
+ if (c->threads <= 1) {
+ return NXT_UNIT_OK;
+ }
+
+ if (c->thread_stack_size > 0) {
+ res = pthread_attr_init(&attr);
+ if (nxt_slow_path(res != 0)) {
+ nxt_unit_alert(NULL, "thread attr init failed: %s (%d)",
+ strerror(res), res);
+
+ return NXT_UNIT_ERROR;
+ }
+
+ res = pthread_attr_setstacksize(&attr, c->thread_stack_size);
+ if (nxt_slow_path(res != 0)) {
+ nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)",
+ strerror(res), res);
+
+ return NXT_UNIT_ERROR;
+ }
+
+ nxt_py_thread_attr = &attr;
+ }
+
+ nxt_py_threads = nxt_unit_malloc(NULL, sizeof(nxt_py_thread_info_t)
+ * (c->threads - 1));
+ if (nxt_slow_path(nxt_py_threads == NULL)) {
+ nxt_unit_alert(NULL, "Failed to allocate thread info array");
+
+ return NXT_UNIT_ERROR;
+ }
+
+ memset(nxt_py_threads, 0, sizeof(nxt_py_thread_info_t) * (c->threads - 1));
+
+ for (i = 0; i < c->threads - 1; i++) {
+ ti = &nxt_py_threads[i];
+
+ res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data);
+ if (nxt_slow_path(res != NXT_UNIT_OK)) {
+ return NXT_UNIT_ERROR;
+ }
+ }
+
+ return NXT_UNIT_OK;
+}
+
+
+static int
+nxt_python_ready_handler(nxt_unit_ctx_t *ctx)
+{
+ int res;
+ uint32_t i;
+ nxt_py_thread_info_t *ti;
+ nxt_python_app_conf_t *c;
+
+ if (nxt_py_proto.ready != NULL) {
+ res = nxt_py_proto.ready(ctx);
+ if (nxt_slow_path(res != NXT_UNIT_OK)) {
+ return NXT_UNIT_ERROR;
+ }
+ }
+
+ /* Worker thread context. */
+ if (!nxt_unit_is_main_ctx(ctx)) {
+ return NXT_UNIT_OK;
+ }
+
+ c = ctx->unit->data;
+
+ if (c->threads <= 1) {
+ return NXT_UNIT_OK;
+ }
+
+ for (i = 0; i < c->threads - 1; i++) {
+ ti = &nxt_py_threads[i];
+
+ ti->ctx = ctx;
+
+ res = pthread_create(&ti->thread, nxt_py_thread_attr,
+ nxt_python_thread_func, ti);
+
+ if (nxt_fast_path(res == 0)) {
+ nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1));
+
+ } else {
+ nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)",
+ (int) (i + 1), strerror(res), res);
+ }
+ }
+
+ return NXT_UNIT_OK;
+}
+
+
+static void *
+nxt_python_thread_func(void *data)
+{
+ nxt_unit_ctx_t *ctx;
+ PyGILState_STATE gstate;
+ nxt_py_thread_info_t *ti;
+
+ ti = data;
+
+ nxt_unit_debug(ti->ctx, "worker thread #%d start",
+ (int) (ti - nxt_py_threads + 1));
+
+ gstate = PyGILState_Ensure();
+
+ if (nxt_py_proto.startup != NULL) {
+ if (nxt_py_proto.startup(ti->ctx_data) != NXT_UNIT_OK) {
+ goto fail;
+ }
+ }
+
+ ctx = nxt_unit_ctx_alloc(ti->ctx, ti->ctx_data);
+ if (nxt_slow_path(ctx == NULL)) {
+ goto fail;
+ }
+
+ (void) nxt_py_proto.run(ctx);
+
+ nxt_unit_done(ctx);
+
+fail:
+
+ PyGILState_Release(gstate);
+
+ nxt_unit_debug(NULL, "worker thread #%d end",
+ (int) (ti - nxt_py_threads + 1));
+
+ return NULL;
+}
+
+
+static void
+nxt_python_join_threads(nxt_unit_ctx_t *ctx, nxt_python_app_conf_t *c)
+{
+ int res;
+ uint32_t i;
+ PyThreadState *thread_state;
+ nxt_py_thread_info_t *ti;
+
+ if (nxt_py_threads == NULL) {
+ return;
+ }
+
+ thread_state = PyEval_SaveThread();
+
+ for (i = 0; i < c->threads - 1; i++) {
+ ti = &nxt_py_threads[i];
+
+ if ((uintptr_t) ti->thread == 0) {
+ continue;
+ }
+
+ res = pthread_join(ti->thread, NULL);
+
+ if (nxt_fast_path(res == 0)) {
+ nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1));
+
+ } else {
+ nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)",
+ (int) (i + 1), strerror(res), res);
+ }
+ }
+
+ PyEval_RestoreThread(thread_state);
+
+ for (i = 0; i < c->threads - 1; i++) {
+ ti = &nxt_py_threads[i];
+
+ if (ti->ctx_data != NULL) {
+ nxt_py_proto.ctx_data_free(ti->ctx_data);
+ }
+ }
+
+ nxt_unit_free(NULL, nxt_py_threads);
+}
+
+
+int
nxt_python_init_strings(nxt_python_string_t *pstr)
{
PyObject *obj;
@@ -271,7 +514,7 @@ nxt_python_init_strings(nxt_python_string_t *pstr)
obj = PyString_FromStringAndSize((char *) pstr->string.start,
pstr->string.length);
if (nxt_slow_path(obj == NULL)) {
- return NXT_ERROR;
+ return NXT_UNIT_ERROR;
}
PyUnicode_InternInPlace(&obj);
@@ -281,7 +524,7 @@ nxt_python_init_strings(nxt_python_string_t *pstr)
pstr++;
}
- return NXT_OK;
+ return NXT_UNIT_OK;
}
@@ -304,8 +547,9 @@ nxt_python_done_strings(nxt_python_string_t *pstr)
static void
nxt_python_atexit(void)
{
- nxt_python_wsgi_done();
- nxt_python_asgi_done();
+ if (nxt_py_proto.done != NULL) {
+ nxt_py_proto.done();
+ }
Py_XDECREF(nxt_py_stderr_flush);
Py_XDECREF(nxt_py_application);
diff --git a/src/python/nxt_python.h b/src/python/nxt_python.h
index 3211026b..b581dd46 100644
--- a/src/python/nxt_python.h
+++ b/src/python/nxt_python.h
@@ -11,6 +11,8 @@
#include <nxt_main.h>
#include <nxt_unit.h>
+#define NXT_PYTHON_VER(maj, min) ((maj << 24) | (min << 16))
+
#if PY_MAJOR_VERSION == 3
#define NXT_PYTHON_BYTES_TYPE "bytestring"
@@ -28,9 +30,10 @@
#define PyBytes_AS_STRING PyString_AS_STRING
#define PyUnicode_InternInPlace PyString_InternInPlace
#define PyUnicode_AsUTF8 PyString_AS_STRING
+#define PyUnicode_GET_LENGTH PyUnicode_GET_SIZE
#endif
-#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 5
+#if PY_VERSION_HEX >= NXT_PYTHON_VER(3, 5)
#define NXT_HAVE_ASGI 1
#endif
@@ -41,20 +44,25 @@ typedef struct {
PyObject **object_p;
} nxt_python_string_t;
+typedef struct {
+ int (*ctx_data_alloc)(void **pdata);
+ void (*ctx_data_free)(void *data);
+ int (*startup)(void *data);
+ int (*run)(nxt_unit_ctx_t *ctx);
+ int (*ready)(nxt_unit_ctx_t *ctx);
+ void (*done)(void);
+} nxt_python_proto_t;
+
-nxt_int_t nxt_python_init_strings(nxt_python_string_t *pstr);
+int nxt_python_init_strings(nxt_python_string_t *pstr);
void nxt_python_done_strings(nxt_python_string_t *pstr);
void nxt_python_print_exception(void);
-nxt_int_t nxt_python_wsgi_init(nxt_task_t *task, nxt_unit_init_t *init);
-int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx);
-void nxt_python_wsgi_done(void);
+int nxt_python_wsgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto);
int nxt_python_asgi_check(PyObject *obj);
-nxt_int_t nxt_python_asgi_init(nxt_task_t *task, nxt_unit_init_t *init);
-nxt_int_t nxt_python_asgi_run(nxt_unit_ctx_t *ctx);
-void nxt_python_asgi_done(void);
+int nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto);
#endif /* _NXT_PYTHON_H_INCLUDED_ */
diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c
index 72408ea1..98aeedf4 100644
--- a/src/python/nxt_python_asgi.c
+++ b/src/python/nxt_python_asgi.c
@@ -16,7 +16,16 @@
#include <python/nxt_python_asgi_str.h>
+static PyObject *nxt_python_asgi_get_func(PyObject *obj);
+static int nxt_python_asgi_ctx_data_alloc(void **pdata);
+static void nxt_python_asgi_ctx_data_free(void *data);
+static int nxt_python_asgi_startup(void *data);
+static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx);
+
+static void nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx,
+ nxt_unit_port_t *port);
static void nxt_py_asgi_request_handler(nxt_unit_request_info_t *req);
+static void nxt_py_asgi_close_handler(nxt_unit_request_info_t *req);
static PyObject *nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req);
static PyObject *nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len,
@@ -24,30 +33,33 @@ static PyObject *nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len,
static PyObject *nxt_py_asgi_create_header(nxt_unit_field_t *f);
static PyObject *nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f);
+static int nxt_python_asgi_ready(nxt_unit_ctx_t *ctx);
+
static int nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port);
static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port);
static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx);
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);
-PyObject *nxt_py_loop_run_until_complete;
-PyObject *nxt_py_loop_create_future;
-PyObject *nxt_py_loop_create_task;
-
-nxt_queue_t nxt_py_asgi_drain_queue;
-
-static PyObject *nxt_py_loop_call_soon;
-static PyObject *nxt_py_quit_future;
-static PyObject *nxt_py_quit_future_set_result;
-static PyObject *nxt_py_loop_add_reader;
-static PyObject *nxt_py_loop_remove_reader;
-static PyObject *nxt_py_port_read;
+int nxt_py_asgi_legacy;
+static PyObject *nxt_py_port_read;
+static nxt_unit_port_t *nxt_py_shared_port;
static PyMethodDef nxt_py_port_read_method =
{"unit_port_read", nxt_py_asgi_port_read, METH_VARARGS, ""};
+static nxt_python_proto_t nxt_py_asgi_proto = {
+ .ctx_data_alloc = nxt_python_asgi_ctx_data_alloc,
+ .ctx_data_free = nxt_python_asgi_ctx_data_free,
+ .startup = nxt_python_asgi_startup,
+ .run = nxt_python_asgi_run,
+ .ready = nxt_python_asgi_ready,
+ .done = nxt_python_asgi_done,
+};
+
#define NXT_UNIT_HASH_WS_PROTOCOL 0xED0A
@@ -55,249 +67,348 @@ int
nxt_python_asgi_check(PyObject *obj)
{
int res;
- PyObject *call;
+ PyObject *func;
PyCodeObject *code;
- if (PyFunction_Check(obj)) {
- code = (PyCodeObject *) PyFunction_GET_CODE(obj);
+ func = nxt_python_asgi_get_func(obj);
+
+ if (func == NULL) {
+ return 0;
+ }
+
+ code = (PyCodeObject *) PyFunction_GET_CODE(func);
+
+ nxt_unit_debug(NULL, "asgi_check: callable is %sa coroutine function with "
+ "%d argument(s)",
+ (code->co_flags & CO_COROUTINE) != 0 ? "" : "not ",
+ code->co_argcount);
+
+ res = (code->co_flags & CO_COROUTINE) != 0 || code->co_argcount == 1;
+
+ Py_DECREF(func);
+
+ return res;
+}
+
+
+static PyObject *
+nxt_python_asgi_get_func(PyObject *obj)
+{
+ PyObject *call;
- return (code->co_flags & CO_COROUTINE) != 0;
+ if (PyFunction_Check(obj)) {
+ Py_INCREF(obj);
+ return obj;
}
if (PyMethod_Check(obj)) {
obj = PyMethod_GET_FUNCTION(obj);
- code = (PyCodeObject *) PyFunction_GET_CODE(obj);
-
- return (code->co_flags & CO_COROUTINE) != 0;
+ Py_INCREF(obj);
+ return obj;
}
call = PyObject_GetAttrString(obj, "__call__");
if (call == NULL) {
- return 0;
+ return NULL;
}
if (PyFunction_Check(call)) {
- code = (PyCodeObject *) PyFunction_GET_CODE(call);
-
- res = (code->co_flags & CO_COROUTINE) != 0;
-
- } else {
- if (PyMethod_Check(call)) {
- obj = PyMethod_GET_FUNCTION(call);
+ return call;
+ }
- code = (PyCodeObject *) PyFunction_GET_CODE(obj);
+ if (PyMethod_Check(call)) {
+ obj = PyMethod_GET_FUNCTION(call);
- res = (code->co_flags & CO_COROUTINE) != 0;
+ Py_INCREF(obj);
+ Py_DECREF(call);
- } else {
- res = 0;
- }
+ return obj;
}
Py_DECREF(call);
- return res;
+ return NULL;
}
-nxt_int_t
-nxt_python_asgi_init(nxt_task_t *task, nxt_unit_init_t *init)
+int
+nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
{
- PyObject *asyncio, *loop, *get_event_loop;
- nxt_int_t rc;
+ PyObject *func;
+ PyCodeObject *code;
- nxt_debug(task, "asgi_init");
+ nxt_unit_debug(NULL, "asgi_init");
- if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_OK)) {
- nxt_alert(task, "Python failed to init string objects");
- return NXT_ERROR;
+ if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_UNIT_OK)) {
+ nxt_unit_alert(NULL, "Python failed to init string objects");
+ return NXT_UNIT_ERROR;
}
- asyncio = PyImport_ImportModule("asyncio");
- if (nxt_slow_path(asyncio == NULL)) {
- nxt_alert(task, "Python failed to import module 'asyncio'");
- nxt_python_print_exception();
- return NXT_ERROR;
+ nxt_py_port_read = PyCFunction_New(&nxt_py_port_read_method, NULL);
+ if (nxt_slow_path(nxt_py_port_read == NULL)) {
+ nxt_unit_alert(NULL,
+ "Python failed to initialize the 'port_read' function");
+ return NXT_UNIT_ERROR;
}
- loop = NULL;
- get_event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio),
- "get_event_loop");
- if (nxt_slow_path(get_event_loop == NULL)) {
- nxt_alert(task,
- "Python failed to get 'get_event_loop' from module 'asyncio'");
- goto fail;
+ if (nxt_slow_path(nxt_py_asgi_http_init() == NXT_UNIT_ERROR)) {
+ return NXT_UNIT_ERROR;
}
- if (nxt_slow_path(PyCallable_Check(get_event_loop) == 0)) {
- nxt_alert(task, "'asyncio.get_event_loop' is not a callable object");
- goto fail;
+ if (nxt_slow_path(nxt_py_asgi_websocket_init() == NXT_UNIT_ERROR)) {
+ return NXT_UNIT_ERROR;
}
- loop = PyObject_CallObject(get_event_loop, NULL);
- if (nxt_slow_path(loop == NULL)) {
- nxt_alert(task, "Python failed to call 'asyncio.get_event_loop'");
- goto fail;
+ 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;
}
- nxt_py_loop_create_task = PyObject_GetAttrString(loop, "create_task");
- if (nxt_slow_path(nxt_py_loop_create_task == NULL)) {
- nxt_alert(task, "Python failed to get 'loop.create_task'");
- goto fail;
- }
+ code = (PyCodeObject *) PyFunction_GET_CODE(func);
- if (nxt_slow_path(PyCallable_Check(nxt_py_loop_create_task) == 0)) {
- nxt_alert(task, "'loop.create_task' is not a callable object");
- goto fail;
+ 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;
}
- nxt_py_loop_add_reader = PyObject_GetAttrString(loop, "add_reader");
- if (nxt_slow_path(nxt_py_loop_add_reader == NULL)) {
- nxt_alert(task, "Python failed to get 'loop.add_reader'");
- goto fail;
- }
+ Py_DECREF(func);
- if (nxt_slow_path(PyCallable_Check(nxt_py_loop_add_reader) == 0)) {
- nxt_alert(task, "'loop.add_reader' is not a callable object");
- goto fail;
- }
+ init->callbacks.request_handler = nxt_py_asgi_request_handler;
+ init->callbacks.data_handler = nxt_py_asgi_http_data_handler;
+ init->callbacks.websocket_handler = nxt_py_asgi_websocket_handler;
+ init->callbacks.close_handler = nxt_py_asgi_close_handler;
+ init->callbacks.quit = nxt_py_asgi_quit;
+ init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler;
+ init->callbacks.add_port = nxt_py_asgi_add_port;
+ init->callbacks.remove_port = nxt_py_asgi_remove_port;
- nxt_py_loop_remove_reader = PyObject_GetAttrString(loop, "remove_reader");
- if (nxt_slow_path(nxt_py_loop_remove_reader == NULL)) {
- nxt_alert(task, "Python failed to get 'loop.remove_reader'");
- goto fail;
- }
+ *proto = nxt_py_asgi_proto;
- if (nxt_slow_path(PyCallable_Check(nxt_py_loop_remove_reader) == 0)) {
- nxt_alert(task, "'loop.remove_reader' is not a callable object");
- goto fail;
- }
+ return NXT_UNIT_OK;
+}
- nxt_py_loop_call_soon = PyObject_GetAttrString(loop, "call_soon");
- if (nxt_slow_path(nxt_py_loop_call_soon == NULL)) {
- nxt_alert(task, "Python failed to get 'loop.call_soon'");
- goto fail;
- }
- if (nxt_slow_path(PyCallable_Check(nxt_py_loop_call_soon) == 0)) {
- nxt_alert(task, "'loop.call_soon' is not a callable object");
- goto fail;
- }
+static int
+nxt_python_asgi_ctx_data_alloc(void **pdata)
+{
+ uint32_t i;
+ PyObject *asyncio, *loop, *new_event_loop, *obj;
+ nxt_py_asgi_ctx_data_t *ctx_data;
- nxt_py_loop_run_until_complete = PyObject_GetAttrString(loop,
- "run_until_complete");
- if (nxt_slow_path(nxt_py_loop_run_until_complete == NULL)) {
- nxt_alert(task, "Python failed to get 'loop.run_until_complete'");
- goto fail;
+ ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t));
+ if (nxt_slow_path(ctx_data == NULL)) {
+ nxt_unit_alert(NULL, "Failed to allocate context data");
+ return NXT_UNIT_ERROR;
}
- if (nxt_slow_path(PyCallable_Check(nxt_py_loop_run_until_complete) == 0)) {
- nxt_alert(task, "'loop.run_until_complete' is not a callable object");
- goto fail;
- }
+ memset(ctx_data, 0, sizeof(nxt_py_asgi_ctx_data_t));
- nxt_py_loop_create_future = PyObject_GetAttrString(loop, "create_future");
- if (nxt_slow_path(nxt_py_loop_create_future == NULL)) {
- nxt_alert(task, "Python failed to get 'loop.create_future'");
- goto fail;
- }
+ nxt_queue_init(&ctx_data->drain_queue);
- if (nxt_slow_path(PyCallable_Check(nxt_py_loop_create_future) == 0)) {
- nxt_alert(task, "'loop.create_future' is not a callable object");
- goto fail;
- }
+ struct {
+ const char *key;
+ PyObject **handler;
+
+ } handlers[] = {
+ { "create_task", &ctx_data->loop_create_task },
+ { "add_reader", &ctx_data->loop_add_reader },
+ { "remove_reader", &ctx_data->loop_remove_reader },
+ { "call_soon", &ctx_data->loop_call_soon },
+ { "run_until_complete", &ctx_data->loop_run_until_complete },
+ { "create_future", &ctx_data->loop_create_future },
+ };
+
+ loop = NULL;
- nxt_py_quit_future = PyObject_CallObject(nxt_py_loop_create_future, NULL);
- if (nxt_slow_path(nxt_py_quit_future == NULL)) {
- nxt_alert(task, "Python failed to create Future ");
+ asyncio = PyImport_ImportModule("asyncio");
+ if (nxt_slow_path(asyncio == NULL)) {
+ nxt_unit_alert(NULL, "Python failed to import module 'asyncio'");
nxt_python_print_exception();
goto fail;
}
- nxt_py_quit_future_set_result = PyObject_GetAttrString(nxt_py_quit_future,
- "set_result");
- if (nxt_slow_path(nxt_py_quit_future_set_result == NULL)) {
- nxt_alert(task, "Python failed to get 'future.set_result'");
+ new_event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio),
+ "new_event_loop");
+ if (nxt_slow_path(new_event_loop == NULL)) {
+ nxt_unit_alert(NULL,
+ "Python failed to get 'new_event_loop' from module 'asyncio'");
goto fail;
}
- if (nxt_slow_path(PyCallable_Check(nxt_py_quit_future_set_result) == 0)) {
- nxt_alert(task, "'future.set_result' is not a callable object");
+ if (nxt_slow_path(PyCallable_Check(new_event_loop) == 0)) {
+ nxt_unit_alert(NULL,
+ "'asyncio.new_event_loop' is not a callable object");
goto fail;
}
- nxt_py_port_read = PyCFunction_New(&nxt_py_port_read_method, NULL);
- if (nxt_slow_path(nxt_py_port_read == NULL)) {
- nxt_alert(task, "Python failed to initialize the 'port_read' function");
+ loop = PyObject_CallObject(new_event_loop, NULL);
+ if (nxt_slow_path(loop == NULL)) {
+ nxt_unit_alert(NULL, "Python failed to call 'asyncio.new_event_loop'");
goto fail;
}
- nxt_queue_init(&nxt_py_asgi_drain_queue);
+ for (i = 0; i < nxt_nitems(handlers); i++) {
+ obj = PyObject_GetAttrString(loop, handlers[i].key);
+ if (nxt_slow_path(obj == NULL)) {
+ nxt_unit_alert(NULL, "Python failed to get 'loop.%s'",
+ handlers[i].key);
+ goto fail;
+ }
+
+ *handlers[i].handler = obj;
- if (nxt_slow_path(nxt_py_asgi_http_init(task) == NXT_ERROR)) {
- goto fail;
+ if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
+ nxt_unit_alert(NULL, "'loop.%s' is not a callable object",
+ handlers[i].key);
+ goto fail;
+ }
}
- if (nxt_slow_path(nxt_py_asgi_websocket_init(task) == NXT_ERROR)) {
+ obj = PyObject_CallObject(ctx_data->loop_create_future, NULL);
+ if (nxt_slow_path(obj == NULL)) {
+ nxt_unit_alert(NULL, "Python failed to create Future ");
+ nxt_python_print_exception();
goto fail;
}
- rc = nxt_py_asgi_lifespan_startup(task);
- if (nxt_slow_path(rc == NXT_ERROR)) {
+ ctx_data->quit_future = obj;
+
+ obj = PyObject_GetAttrString(ctx_data->quit_future, "set_result");
+ if (nxt_slow_path(obj == NULL)) {
+ nxt_unit_alert(NULL, "Python failed to get 'future.set_result'");
goto fail;
}
- init->callbacks.request_handler = nxt_py_asgi_request_handler;
- init->callbacks.data_handler = nxt_py_asgi_http_data_handler;
- init->callbacks.websocket_handler = nxt_py_asgi_websocket_handler;
- init->callbacks.close_handler = nxt_py_asgi_websocket_close_handler;
- init->callbacks.quit = nxt_py_asgi_quit;
- init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler;
- init->callbacks.add_port = nxt_py_asgi_add_port;
- init->callbacks.remove_port = nxt_py_asgi_remove_port;
+ ctx_data->quit_future_set_result = obj;
+
+ if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
+ nxt_unit_alert(NULL, "'future.set_result' is not a callable object");
+ goto fail;
+ }
Py_DECREF(loop);
Py_DECREF(asyncio);
- return NXT_OK;
+ *pdata = ctx_data;
+
+ return NXT_UNIT_OK;
fail:
+ nxt_python_asgi_ctx_data_free(ctx_data);
+
Py_XDECREF(loop);
- Py_DECREF(asyncio);
+ Py_XDECREF(asyncio);
+
+ return NXT_UNIT_ERROR;
+}
+
+
+static void
+nxt_python_asgi_ctx_data_free(void *data)
+{
+ nxt_py_asgi_ctx_data_t *ctx_data;
+
+ ctx_data = data;
- return NXT_ERROR;
+ Py_XDECREF(ctx_data->loop_run_until_complete);
+ Py_XDECREF(ctx_data->loop_create_future);
+ Py_XDECREF(ctx_data->loop_create_task);
+ Py_XDECREF(ctx_data->loop_call_soon);
+ Py_XDECREF(ctx_data->loop_add_reader);
+ Py_XDECREF(ctx_data->loop_remove_reader);
+ Py_XDECREF(ctx_data->quit_future);
+ Py_XDECREF(ctx_data->quit_future_set_result);
+
+ nxt_unit_free(NULL, ctx_data);
}
-nxt_int_t
+static int
+nxt_python_asgi_startup(void *data)
+{
+ return nxt_py_asgi_lifespan_startup(data);
+}
+
+
+static int
nxt_python_asgi_run(nxt_unit_ctx_t *ctx)
{
- PyObject *res;
+ PyObject *res;
+ nxt_py_asgi_ctx_data_t *ctx_data;
- res = PyObject_CallFunctionObjArgs(nxt_py_loop_run_until_complete,
- nxt_py_quit_future, NULL);
+ ctx_data = ctx->data;
+
+ res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete,
+ ctx_data->quit_future, NULL);
if (nxt_slow_path(res == NULL)) {
nxt_unit_alert(ctx, "Python failed to call loop.run_until_complete");
nxt_python_print_exception();
- return NXT_ERROR;
+ return NXT_UNIT_ERROR;
}
Py_DECREF(res);
- nxt_py_asgi_lifespan_shutdown();
+ nxt_py_asgi_remove_reader(ctx, nxt_py_shared_port);
+ nxt_py_asgi_remove_reader(ctx, ctx_data->port);
+
+ if (ctx_data->port != NULL) {
+ ctx_data->port->data = NULL;
+ ctx_data->port = NULL;
+ }
+
+ nxt_py_asgi_lifespan_shutdown(ctx);
+
+ return NXT_UNIT_OK;
+}
+
+
+static void
+nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
+{
+ PyObject *res, *fd;
+ nxt_py_asgi_ctx_data_t *ctx_data;
+
+ if (port == NULL || port->in_fd == -1) {
+ return;
+ }
+
+ ctx_data = ctx->data;
+
+ nxt_unit_debug(ctx, "asgi_remove_reader %d %p", port->in_fd, port);
+
+ fd = PyLong_FromLong(port->in_fd);
+ if (nxt_slow_path(fd == NULL)) {
+ nxt_unit_alert(ctx, "Python failed to create Long object");
+ nxt_python_print_exception();
+
+ return;
+ }
+
+ res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, fd, NULL);
+ if (nxt_slow_path(res == NULL)) {
+ nxt_unit_alert(ctx, "Python failed to remove_reader");
+ nxt_python_print_exception();
+
+ } else {
+ Py_DECREF(res);
+ }
- return NXT_OK;
+ Py_DECREF(fd);
}
static void
nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
{
- PyObject *scope, *res, *task, *receive, *send, *done, *asgi;
+ PyObject *scope, *res, *task, *receive, *send, *done, *asgi;
+ PyObject *stage2;
+ nxt_py_asgi_ctx_data_t *ctx_data;
if (req->request->websocket_handshake) {
asgi = nxt_py_asgi_websocket_create(req);
@@ -346,8 +457,42 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
req->data = asgi;
- res = PyObject_CallFunctionObjArgs(nxt_py_application,
- scope, receive, send, NULL);
+ if (!nxt_py_asgi_legacy) {
+ nxt_unit_req_debug(req, "Python call ASGI 3.0 application");
+
+ res = PyObject_CallFunctionObjArgs(nxt_py_application,
+ scope, receive, send, NULL);
+
+ } else {
+ nxt_unit_req_debug(req, "Python call legacy application");
+
+ res = PyObject_CallFunctionObjArgs(nxt_py_application, scope, NULL);
+
+ if (nxt_slow_path(res == NULL)) {
+ nxt_unit_req_error(req, "Python failed to call legacy app stage1");
+ nxt_python_print_exception();
+ nxt_unit_request_done(req, NXT_UNIT_ERROR);
+
+ goto release_scope;
+ }
+
+ if (nxt_slow_path(PyCallable_Check(res) == 0)) {
+ nxt_unit_req_error(req,
+ "Legacy ASGI application returns not a callable");
+ nxt_unit_request_done(req, NXT_UNIT_ERROR);
+
+ Py_DECREF(res);
+
+ goto release_scope;
+ }
+
+ stage2 = res;
+
+ res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL);
+
+ Py_DECREF(stage2);
+ }
+
if (nxt_slow_path(res == NULL)) {
nxt_unit_req_error(req, "Python failed to call the application");
nxt_python_print_exception();
@@ -365,7 +510,9 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req)
goto release_scope;
}
- task = PyObject_CallFunctionObjArgs(nxt_py_loop_create_task, res, NULL);
+ ctx_data = req->ctx->data;
+
+ task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, NULL);
if (nxt_slow_path(task == NULL)) {
nxt_unit_req_error(req, "Python failed to call the create_task");
nxt_python_print_exception();
@@ -405,6 +552,18 @@ release_asgi:
}
+static void
+nxt_py_asgi_close_handler(nxt_unit_request_info_t *req)
+{
+ if (req->request->websocket_handshake) {
+ nxt_py_asgi_websocket_close_handler(req);
+
+ } else {
+ nxt_py_asgi_http_close_handler(req);
+ }
+}
+
+
static PyObject *
nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req)
{
@@ -724,10 +883,82 @@ fail:
static int
+nxt_python_asgi_ready(nxt_unit_ctx_t *ctx)
+{
+ int rc;
+ PyObject *res, *fd, *py_ctx, *py_port;
+ nxt_unit_port_t *port;
+ nxt_py_asgi_ctx_data_t *ctx_data;
+
+ if (nxt_slow_path(nxt_py_shared_port == NULL)) {
+ return NXT_UNIT_ERROR;
+ }
+
+ port = nxt_py_shared_port;
+
+ nxt_unit_debug(ctx, "asgi_ready %d %p %p", port->in_fd, ctx, port);
+
+ ctx_data = ctx->data;
+
+ rc = NXT_UNIT_ERROR;
+
+ fd = PyLong_FromLong(port->in_fd);
+ if (nxt_slow_path(fd == NULL)) {
+ nxt_unit_alert(ctx, "Python failed to create fd");
+ nxt_python_print_exception();
+
+ return rc;
+ }
+
+ py_ctx = PyLong_FromVoidPtr(ctx);
+ if (nxt_slow_path(py_ctx == NULL)) {
+ nxt_unit_alert(ctx, "Python failed to create py_ctx");
+ nxt_python_print_exception();
+
+ goto clean_fd;
+ }
+
+ py_port = PyLong_FromVoidPtr(port);
+ if (nxt_slow_path(py_port == NULL)) {
+ nxt_unit_alert(ctx, "Python failed to create py_port");
+ nxt_python_print_exception();
+
+ goto clean_py_ctx;
+ }
+
+ res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader,
+ fd, nxt_py_port_read,
+ py_ctx, py_port, NULL);
+ if (nxt_slow_path(res == NULL)) {
+ nxt_unit_alert(ctx, "Python failed to add_reader");
+ nxt_python_print_exception();
+
+ } else {
+ Py_DECREF(res);
+
+ rc = NXT_UNIT_OK;
+ }
+
+ Py_DECREF(py_port);
+
+clean_py_ctx:
+
+ Py_DECREF(py_ctx);
+
+clean_fd:
+
+ Py_DECREF(fd);
+
+ return rc;
+}
+
+
+static int
nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
{
- int nb;
- PyObject *res;
+ int nb, rc;
+ PyObject *res, *fd, *py_ctx, *py_port;
+ nxt_py_asgi_ctx_data_t *ctx_data;
if (port->in_fd == -1) {
return NXT_UNIT_OK;
@@ -744,73 +975,152 @@ nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port)
nxt_unit_debug(ctx, "asgi_add_port %d %p %p", port->in_fd, ctx, port);
- res = PyObject_CallFunctionObjArgs(nxt_py_loop_add_reader,
- PyLong_FromLong(port->in_fd),
- nxt_py_port_read,
- PyLong_FromVoidPtr(ctx),
- PyLong_FromVoidPtr(port), NULL);
+ if (port->id.id == NXT_UNIT_SHARED_PORT_ID) {
+ nxt_py_shared_port = port;
+
+ return NXT_UNIT_OK;
+ }
+
+ ctx_data = ctx->data;
+
+ ctx_data->port = port;
+ port->data = ctx_data;
+
+ rc = NXT_UNIT_ERROR;
+
+ fd = PyLong_FromLong(port->in_fd);
+ if (nxt_slow_path(fd == NULL)) {
+ nxt_unit_alert(ctx, "Python failed to create fd");
+ nxt_python_print_exception();
+
+ return rc;
+ }
+
+ py_ctx = PyLong_FromVoidPtr(ctx);
+ if (nxt_slow_path(py_ctx == NULL)) {
+ nxt_unit_alert(ctx, "Python failed to create py_ctx");
+ nxt_python_print_exception();
+
+ goto clean_fd;
+ }
+
+ py_port = PyLong_FromVoidPtr(port);
+ if (nxt_slow_path(py_port == NULL)) {
+ nxt_unit_alert(ctx, "Python failed to create py_port");
+ nxt_python_print_exception();
+
+ goto clean_py_ctx;
+ }
+
+ res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader,
+ fd, nxt_py_port_read,
+ py_ctx, py_port, NULL);
if (nxt_slow_path(res == NULL)) {
nxt_unit_alert(ctx, "Python failed to add_reader");
+ nxt_python_print_exception();
- return NXT_UNIT_ERROR;
+ } else {
+ Py_DECREF(res);
+
+ rc = NXT_UNIT_OK;
}
- Py_DECREF(res);
+ Py_DECREF(py_port);
- return NXT_UNIT_OK;
+clean_py_ctx:
+
+ Py_DECREF(py_ctx);
+
+clean_fd:
+
+ Py_DECREF(fd);
+
+ return rc;
}
static void
nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port)
{
- PyObject *res;
-
- nxt_unit_debug(NULL, "asgi_remove_port %d %p", port->in_fd, port);
-
if (port->in_fd == -1) {
return;
}
- res = PyObject_CallFunctionObjArgs(nxt_py_loop_remove_reader,
- PyLong_FromLong(port->in_fd), NULL);
- if (nxt_slow_path(res == NULL)) {
- nxt_unit_alert(NULL, "Python failed to remove_reader");
- }
+ nxt_unit_debug(NULL, "asgi_remove_port %d %p", port->in_fd, port);
- Py_DECREF(res);
+ if (nxt_py_shared_port == port) {
+ nxt_py_shared_port = NULL;
+ }
}
static void
nxt_py_asgi_quit(nxt_unit_ctx_t *ctx)
{
- PyObject *res;
+ PyObject *res, *p;
+ nxt_py_asgi_ctx_data_t *ctx_data;
nxt_unit_debug(ctx, "asgi_quit %p", ctx);
- res = PyObject_CallFunctionObjArgs(nxt_py_quit_future_set_result,
- PyLong_FromLong(0), NULL);
- if (nxt_slow_path(res == NULL)) {
- nxt_unit_alert(ctx, "Python failed to set_result");
+ ctx_data = ctx->data;
+
+ if (nxt_py_shared_port != NULL) {
+ p = PyLong_FromLong(nxt_py_shared_port->in_fd);
+ if (nxt_slow_path(p == NULL)) {
+ nxt_unit_alert(NULL, "Python failed to create Long");
+ nxt_python_print_exception();
+
+ } else {
+ res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader,
+ p, NULL);
+ if (nxt_slow_path(res == NULL)) {
+ nxt_unit_alert(NULL, "Python failed to remove_reader");
+ nxt_python_print_exception();
+
+ } else {
+ Py_DECREF(res);
+ }
+
+ Py_DECREF(p);
+ }
}
- Py_DECREF(res);
+ p = PyLong_FromLong(0);
+ if (nxt_slow_path(p == NULL)) {
+ nxt_unit_alert(NULL, "Python failed to create Long");
+ nxt_python_print_exception();
+
+ } else {
+ res = PyObject_CallFunctionObjArgs(ctx_data->quit_future_set_result,
+ p, NULL);
+ if (nxt_slow_path(res == NULL)) {
+ nxt_unit_alert(ctx, "Python failed to set_result");
+ nxt_python_print_exception();
+
+ } else {
+ Py_DECREF(res);
+ }
+
+ Py_DECREF(p);
+ }
}
static void
nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx)
{
- int rc;
- nxt_queue_link_t *lnk;
+ int rc;
+ nxt_queue_link_t *lnk;
+ nxt_py_asgi_ctx_data_t *ctx_data;
- while (!nxt_queue_is_empty(&nxt_py_asgi_drain_queue)) {
- lnk = nxt_queue_first(&nxt_py_asgi_drain_queue);
+ ctx_data = ctx->data;
+
+ while (!nxt_queue_is_empty(&ctx_data->drain_queue)) {
+ lnk = nxt_queue_first(&ctx_data->drain_queue);
rc = nxt_py_asgi_http_drain(lnk);
if (rc == NXT_UNIT_AGAIN) {
- break;
+ return;
}
nxt_queue_remove(lnk);
@@ -859,7 +1169,7 @@ nxt_py_asgi_port_read(PyObject *self, PyObject *args)
if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
return PyErr_Format(PyExc_RuntimeError,
- "error processing port message");
+ "error processing port %d message", port->id.id);
}
Py_RETURN_NONE;
@@ -996,8 +1306,8 @@ nxt_py_asgi_add_field(void *data, int i, PyObject *name, PyObject *val)
PyObject *
-nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, PyObject *future,
- PyObject *result)
+nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req,
+ nxt_py_asgi_ctx_data_t *ctx_data, PyObject *future, PyObject *result)
{
PyObject *set_result, *res;
@@ -1013,7 +1323,7 @@ nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, PyObject *future,
Py_CLEAR(future);
- goto cleanup;
+ goto cleanup_result;
}
if (nxt_slow_path(PyCallable_Check(set_result) == 0)) {
@@ -1024,7 +1334,7 @@ nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, PyObject *future,
goto cleanup;
}
- res = PyObject_CallFunctionObjArgs(nxt_py_loop_call_soon, set_result,
+ res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, set_result,
result, NULL);
if (nxt_slow_path(res == NULL)) {
nxt_unit_req_alert(req, "Python failed to call 'loop.call_soon'");
@@ -1038,6 +1348,9 @@ nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, PyObject *future,
cleanup:
Py_DECREF(set_result);
+
+cleanup_result:
+
Py_DECREF(result);
return future;
@@ -1148,6 +1461,17 @@ nxt_py_asgi_new_scope(nxt_unit_request_info_t *req, PyObject *type,
void
+nxt_py_asgi_drain_wait(nxt_unit_request_info_t *req, nxt_queue_link_t *link)
+{
+ nxt_py_asgi_ctx_data_t *ctx_data;
+
+ ctx_data = req->ctx->data;
+
+ nxt_queue_insert_tail(&ctx_data->drain_queue, link);
+}
+
+
+void
nxt_py_asgi_dealloc(PyObject *self)
{
PyObject_Del(self);
@@ -1177,19 +1501,11 @@ nxt_py_asgi_next(PyObject *self)
}
-void
+static void
nxt_python_asgi_done(void)
{
nxt_py_asgi_str_done();
- Py_XDECREF(nxt_py_quit_future);
- Py_XDECREF(nxt_py_quit_future_set_result);
- Py_XDECREF(nxt_py_loop_run_until_complete);
- Py_XDECREF(nxt_py_loop_create_future);
- Py_XDECREF(nxt_py_loop_create_task);
- Py_XDECREF(nxt_py_loop_call_soon);
- Py_XDECREF(nxt_py_loop_add_reader);
- Py_XDECREF(nxt_py_loop_remove_reader);
Py_XDECREF(nxt_py_port_read);
}
@@ -1203,25 +1519,12 @@ nxt_python_asgi_check(PyObject *obj)
}
-nxt_int_t
-nxt_python_asgi_init(nxt_task_t *task, nxt_unit_init_t *init)
-{
- nxt_alert(task, "ASGI not implemented");
- return NXT_ERROR;
-}
-
-
-nxt_int_t
-nxt_python_asgi_run(nxt_unit_ctx_t *ctx)
+int
+nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
{
- nxt_unit_alert(ctx, "ASGI not implemented");
- return NXT_ERROR;
+ nxt_unit_alert(NULL, "ASGI not implemented");
+ return NXT_UNIT_ERROR;
}
-void
-nxt_python_asgi_done(void)
-{
-}
-
#endif /* NXT_HAVE_ASGI */
diff --git a/src/python/nxt_python_asgi.h b/src/python/nxt_python_asgi.h
index 24337c37..37f2a099 100644
--- a/src/python/nxt_python_asgi.h
+++ b/src/python/nxt_python_asgi.h
@@ -10,6 +10,9 @@
typedef PyObject * (*nxt_py_asgi_enum_header_cb)(void *ctx, int i,
PyObject *name, PyObject *val);
+void nxt_py_asgi_drain_wait(nxt_unit_request_info_t *req,
+ nxt_queue_link_t *link);
+
typedef struct {
uint32_t fields_count;
uint32_t fields_size;
@@ -20,6 +23,20 @@ typedef struct {
uint64_t content_length;
} nxt_py_asgi_add_field_ctx_t;
+typedef struct {
+ nxt_queue_t drain_queue;
+ PyObject *loop_run_until_complete;
+ PyObject *loop_create_future;
+ PyObject *loop_create_task;
+ PyObject *loop_call_soon;
+ PyObject *loop_add_reader;
+ PyObject *loop_remove_reader;
+ PyObject *quit_future;
+ PyObject *quit_future_set_result;
+ PyObject *lifespan;
+ nxt_unit_port_t *port;
+} nxt_py_asgi_ctx_data_t;
+
PyObject *nxt_py_asgi_enum_headers(PyObject *headers,
nxt_py_asgi_enum_header_cb cb, void *data);
@@ -27,7 +44,7 @@ PyObject *nxt_py_asgi_calc_size(void *data, int i, PyObject *n, PyObject *v);
PyObject *nxt_py_asgi_add_field(void *data, int i, PyObject *n, PyObject *v);
PyObject *nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req,
- PyObject *future, PyObject *result);
+ nxt_py_asgi_ctx_data_t *ctx_data, PyObject *future, PyObject *result);
PyObject *nxt_py_asgi_new_msg(nxt_unit_request_info_t *req, PyObject *type);
PyObject *nxt_py_asgi_new_scope(nxt_unit_request_info_t *req, PyObject *type,
PyObject *spec_version);
@@ -37,24 +54,21 @@ PyObject *nxt_py_asgi_await(PyObject *self);
PyObject *nxt_py_asgi_iter(PyObject *self);
PyObject *nxt_py_asgi_next(PyObject *self);
-nxt_int_t nxt_py_asgi_http_init(nxt_task_t *task);
+int nxt_py_asgi_http_init(void);
PyObject *nxt_py_asgi_http_create(nxt_unit_request_info_t *req);
void nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req);
int nxt_py_asgi_http_drain(nxt_queue_link_t *lnk);
+void nxt_py_asgi_http_close_handler(nxt_unit_request_info_t *req);
-nxt_int_t nxt_py_asgi_websocket_init(nxt_task_t *task);
+int nxt_py_asgi_websocket_init(void);
PyObject *nxt_py_asgi_websocket_create(nxt_unit_request_info_t *req);
void nxt_py_asgi_websocket_handler(nxt_unit_websocket_frame_t *ws);
void nxt_py_asgi_websocket_close_handler(nxt_unit_request_info_t *req);
-nxt_int_t nxt_py_asgi_lifespan_startup(nxt_task_t *task);
-nxt_int_t nxt_py_asgi_lifespan_shutdown(void);
-
-extern PyObject *nxt_py_loop_run_until_complete;
-extern PyObject *nxt_py_loop_create_future;
-extern PyObject *nxt_py_loop_create_task;
+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 nxt_queue_t nxt_py_asgi_drain_queue;
+extern int nxt_py_asgi_legacy;
#endif /* _NXT_PYTHON_ASGI_H_INCLUDED_ */
diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c
index b07d61d6..d88c4b00 100644
--- a/src/python/nxt_python_asgi_http.c
+++ b/src/python/nxt_python_asgi_http.c
@@ -24,6 +24,7 @@ typedef struct {
uint64_t content_length;
uint64_t bytes_sent;
int complete;
+ int closed;
PyObject *send_body;
Py_ssize_t send_body_off;
} nxt_py_asgi_http_t;
@@ -67,15 +68,16 @@ static PyTypeObject nxt_py_asgi_http_type = {
static Py_ssize_t nxt_py_asgi_http_body_buf_size = 32 * 1024 * 1024;
-nxt_int_t
-nxt_py_asgi_http_init(nxt_task_t *task)
+int
+nxt_py_asgi_http_init(void)
{
if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_http_type) != 0)) {
- nxt_alert(task, "Python failed to initialize the 'http' type object");
- return NXT_ERROR;
+ nxt_unit_alert(NULL,
+ "Python failed to initialize the 'http' type object");
+ return NXT_UNIT_ERROR;
}
- return NXT_OK;
+ return NXT_UNIT_OK;
}
@@ -93,6 +95,7 @@ nxt_py_asgi_http_create(nxt_unit_request_info_t *req)
http->content_length = -1;
http->bytes_sent = 0;
http->complete = 0;
+ http->closed = 0;
http->send_body = NULL;
http->send_body_off = 0;
}
@@ -106,6 +109,7 @@ nxt_py_asgi_http_receive(PyObject *self, PyObject *none)
{
PyObject *msg, *future;
nxt_py_asgi_http_t *http;
+ nxt_py_asgi_ctx_data_t *ctx_data;
nxt_unit_request_info_t *req;
http = (nxt_py_asgi_http_t *) self;
@@ -113,12 +117,20 @@ nxt_py_asgi_http_receive(PyObject *self, PyObject *none)
nxt_unit_req_debug(req, "asgi_http_receive");
- msg = nxt_py_asgi_http_read_msg(http);
+ if (nxt_slow_path(http->closed || nxt_unit_response_is_sent(req))) {
+ msg = nxt_py_asgi_new_msg(req, nxt_py_http_disconnect_str);
+
+ } else {
+ msg = nxt_py_asgi_http_read_msg(http);
+ }
+
if (nxt_slow_path(msg == NULL)) {
return NULL;
}
- future = PyObject_CallObject(nxt_py_loop_create_future, NULL);
+ ctx_data = req->ctx->data;
+
+ future = PyObject_CallObject(ctx_data->loop_create_future, NULL);
if (nxt_slow_path(future == NULL)) {
nxt_unit_req_alert(req, "Python failed to create Future object");
nxt_python_print_exception();
@@ -130,7 +142,7 @@ nxt_py_asgi_http_receive(PyObject *self, PyObject *none)
}
if (msg != Py_None) {
- return nxt_py_asgi_set_result_soon(req, future, msg);
+ return nxt_py_asgi_set_result_soon(req, ctx_data, future, msg);
}
http->receive_future = future;
@@ -250,22 +262,23 @@ nxt_py_asgi_http_send(PyObject *self, PyObject *dict)
nxt_unit_req_debug(http->req, "asgi_http_send type is '%.*s'",
(int) type_len, type_str);
- if (type_len == (Py_ssize_t) response_start.length
- && memcmp(type_str, response_start.start, type_len) == 0)
- {
- return nxt_py_asgi_http_response_start(http, dict);
- }
+ if (nxt_unit_response_is_init(http->req)) {
+ if (nxt_str_eq(&response_body, type_str, (size_t) type_len)) {
+ return nxt_py_asgi_http_response_body(http, dict);
+ }
- if (type_len == (Py_ssize_t) response_body.length
- && memcmp(type_str, response_body.start, type_len) == 0)
- {
- return nxt_py_asgi_http_response_body(http, dict);
+ return PyErr_Format(PyExc_RuntimeError,
+ "Expected ASGI message 'http.response.body', "
+ "but got '%U'", type);
}
- nxt_unit_req_error(http->req, "asgi_http_send: unexpected 'type': '%.*s'",
- (int) type_len, type_str);
+ if (nxt_str_eq(&response_start, type_str, (size_t) type_len)) {
+ return nxt_py_asgi_http_response_start(http, dict);
+ }
- return PyErr_Format(PyExc_AssertionError, "unexpected 'type': '%U'", type);
+ return PyErr_Format(PyExc_RuntimeError,
+ "Expected ASGI message 'http.response.start', "
+ "but got '%U'", type);
}
@@ -329,11 +342,12 @@ nxt_py_asgi_http_response_start(nxt_py_asgi_http_t *http, PyObject *dict)
static PyObject *
nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict)
{
- int rc;
- char *body_str;
- ssize_t sent;
- PyObject *body, *more_body, *future;
- Py_ssize_t body_len, body_off;
+ int rc;
+ char *body_str;
+ ssize_t sent;
+ PyObject *body, *more_body, *future;
+ Py_ssize_t body_len, body_off;
+ nxt_py_asgi_ctx_data_t *ctx_data;
body = PyDict_GetItem(dict, nxt_py_body_str);
if (nxt_slow_path(body != NULL && !PyBytes_Check(body))) {
@@ -371,6 +385,8 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict)
body_off = 0;
+ ctx_data = http->req->ctx->data;
+
while (body_len > 0) {
sent = nxt_unit_response_write_nb(http->req, body_str, body_len, 0);
if (nxt_slow_path(sent < 0)) {
@@ -382,7 +398,8 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict)
"out of shared memory, %d",
(int) body_len);
- future = PyObject_CallObject(nxt_py_loop_create_future, NULL);
+ future = PyObject_CallObject(ctx_data->loop_create_future,
+ NULL);
if (nxt_slow_path(future == NULL)) {
nxt_unit_req_alert(http->req,
"Python failed to create Future object");
@@ -396,7 +413,7 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict)
Py_INCREF(http->send_body);
http->send_body_off = body_off;
- nxt_queue_insert_tail(&nxt_py_asgi_drain_queue, &http->link);
+ nxt_py_asgi_drain_wait(http->req, &http->link);
http->send_future = future;
Py_INCREF(http->send_future);
@@ -553,6 +570,48 @@ fail:
}
+void
+nxt_py_asgi_http_close_handler(nxt_unit_request_info_t *req)
+{
+ PyObject *msg, *future, *res;
+ nxt_py_asgi_http_t *http;
+
+ http = req->data;
+
+ nxt_unit_req_debug(req, "asgi_http_close_handler");
+
+ http->closed = 1;
+
+ if (http->receive_future == NULL) {
+ return;
+ }
+
+ msg = nxt_py_asgi_new_msg(req, nxt_py_http_disconnect_str);
+ if (nxt_slow_path(msg == NULL)) {
+ return;
+ }
+
+ if (msg == Py_None) {
+ Py_DECREF(msg);
+ return;
+ }
+
+ future = http->receive_future;
+ http->receive_future = NULL;
+
+ res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL);
+ if (nxt_slow_path(res == NULL)) {
+ nxt_unit_req_alert(req, "'set_result' call failed");
+ nxt_python_print_exception();
+ }
+
+ Py_XDECREF(res);
+ Py_DECREF(future);
+
+ Py_DECREF(msg);
+}
+
+
static PyObject *
nxt_py_asgi_http_done(PyObject *self, PyObject *future)
{
diff --git a/src/python/nxt_python_asgi_lifespan.c b/src/python/nxt_python_asgi_lifespan.c
index 14d0ee97..506eaf4d 100644
--- a/src/python/nxt_python_asgi_lifespan.c
+++ b/src/python/nxt_python_asgi_lifespan.c
@@ -15,15 +15,16 @@
typedef struct {
PyObject_HEAD
- int disabled;
- int startup_received;
- int startup_sent;
- int shutdown_received;
- int shutdown_sent;
- int shutdown_called;
- PyObject *startup_future;
- PyObject *shutdown_future;
- PyObject *receive_future;
+ nxt_py_asgi_ctx_data_t *ctx_data;
+ int disabled;
+ int startup_received;
+ int startup_sent;
+ int shutdown_received;
+ int shutdown_sent;
+ int shutdown_called;
+ PyObject *startup_future;
+ PyObject *shutdown_future;
+ PyObject *receive_future;
} nxt_py_asgi_lifespan_t;
@@ -39,8 +40,6 @@ static PyObject *nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan);
static PyObject *nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future);
-static nxt_py_asgi_lifespan_t *nxt_py_lifespan;
-
static PyMethodDef nxt_py_asgi_lifespan_methods[] = {
{ "receive", nxt_py_asgi_lifespan_receive, METH_NOARGS, 0 },
{ "send", nxt_py_asgi_lifespan_send, METH_O, 0 },
@@ -67,46 +66,47 @@ static PyTypeObject nxt_py_asgi_lifespan_type = {
};
-nxt_int_t
-nxt_py_asgi_lifespan_startup(nxt_task_t *task)
+int
+nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data)
{
+ int rc;
PyObject *scope, *res, *py_task, *receive, *send, *done;
- nxt_int_t rc;
+ PyObject *stage2;
nxt_py_asgi_lifespan_t *lifespan;
if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_lifespan_type) != 0)) {
- nxt_alert(task,
+ nxt_unit_alert(NULL,
"Python failed to initialize the 'asgi_lifespan' type object");
- return NXT_ERROR;
+ return NXT_UNIT_ERROR;
}
lifespan = PyObject_New(nxt_py_asgi_lifespan_t, &nxt_py_asgi_lifespan_type);
if (nxt_slow_path(lifespan == NULL)) {
- nxt_alert(task, "Python failed to create lifespan object");
- return NXT_ERROR;
+ nxt_unit_alert(NULL, "Python failed to create lifespan object");
+ return NXT_UNIT_ERROR;
}
- rc = NXT_ERROR;
+ rc = NXT_UNIT_ERROR;
receive = PyObject_GetAttrString((PyObject *) lifespan, "receive");
if (nxt_slow_path(receive == NULL)) {
- nxt_alert(task, "Python failed to get 'receive' method");
+ nxt_unit_alert(NULL, "Python failed to get 'receive' method");
goto release_lifespan;
}
send = PyObject_GetAttrString((PyObject *) lifespan, "send");
if (nxt_slow_path(receive == NULL)) {
- nxt_alert(task, "Python failed to get 'send' method");
+ nxt_unit_alert(NULL, "Python failed to get 'send' method");
goto release_receive;
}
done = PyObject_GetAttrString((PyObject *) lifespan, "_done");
if (nxt_slow_path(receive == NULL)) {
- nxt_alert(task, "Python failed to get '_done' method");
+ nxt_unit_alert(NULL, "Python failed to get '_done' method");
goto release_send;
}
- lifespan->startup_future = PyObject_CallObject(nxt_py_loop_create_future,
+ lifespan->startup_future = PyObject_CallObject(ctx_data->loop_create_future,
NULL);
if (nxt_slow_path(lifespan->startup_future == NULL)) {
nxt_unit_alert(NULL, "Python failed to create Future object");
@@ -115,6 +115,7 @@ nxt_py_asgi_lifespan_startup(nxt_task_t *task)
goto release_done;
}
+ lifespan->ctx_data = ctx_data;
lifespan->disabled = 0;
lifespan->startup_received = 0;
lifespan->startup_sent = 0;
@@ -129,24 +130,59 @@ nxt_py_asgi_lifespan_startup(nxt_task_t *task)
goto release_future;
}
- res = PyObject_CallFunctionObjArgs(nxt_py_application,
- scope, receive, send, NULL);
+ if (!nxt_py_asgi_legacy) {
+ nxt_unit_req_debug(NULL, "Python call ASGI 3.0 application");
+
+ res = PyObject_CallFunctionObjArgs(nxt_py_application,
+ scope, receive, send, NULL);
+
+ } else {
+ nxt_unit_req_debug(NULL, "Python call legacy application");
+
+ res = PyObject_CallFunctionObjArgs(nxt_py_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;
+
+ goto release_scope;
+ }
+
+ if (nxt_slow_path(PyCallable_Check(res) == 0)) {
+ nxt_unit_req_error(NULL,
+ "Legacy ASGI application returns not a callable");
+
+ Py_DECREF(res);
+
+ goto release_scope;
+ }
+
+ stage2 = res;
+
+ res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL);
+
+ Py_DECREF(stage2);
+ }
+
if (nxt_slow_path(res == NULL)) {
- nxt_log(task, NXT_LOG_ERR, "Python failed to call the application");
+ nxt_unit_error(NULL, "Python failed to call the application");
nxt_python_print_exception();
goto release_scope;
}
if (nxt_slow_path(!PyCoro_CheckExact(res))) {
- nxt_log(task, NXT_LOG_ERR,
- "Application result type is not a coroutine");
+ nxt_unit_error(NULL, "Application result type is not a coroutine");
Py_DECREF(res);
goto release_scope;
}
- py_task = PyObject_CallFunctionObjArgs(nxt_py_loop_create_task, res, NULL);
+ py_task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res,
+ NULL);
if (nxt_slow_path(py_task == NULL)) {
- nxt_log(task, NXT_LOG_ERR, "Python failed to call the create_task");
+ nxt_unit_alert(NULL, "Python failed to call the create_task");
nxt_python_print_exception();
Py_DECREF(res);
goto release_scope;
@@ -157,18 +193,17 @@ nxt_py_asgi_lifespan_startup(nxt_task_t *task)
res = PyObject_CallMethodObjArgs(py_task, nxt_py_add_done_callback_str,
done, NULL);
if (nxt_slow_path(res == NULL)) {
- nxt_log(task, NXT_LOG_ERR,
- "Python failed to call 'task.add_done_callback'");
+ nxt_unit_alert(NULL, "Python failed to call 'task.add_done_callback'");
nxt_python_print_exception();
goto release_task;
}
Py_DECREF(res);
- res = PyObject_CallFunctionObjArgs(nxt_py_loop_run_until_complete,
+ res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete,
lifespan->startup_future, NULL);
if (nxt_slow_path(res == NULL)) {
- nxt_alert(task, "Python failed to call loop.run_until_complete");
+ nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete");
nxt_python_print_exception();
goto release_task;
}
@@ -176,10 +211,10 @@ nxt_py_asgi_lifespan_startup(nxt_task_t *task)
Py_DECREF(res);
if (lifespan->startup_sent == 1 || lifespan->disabled) {
- nxt_py_lifespan = lifespan;
- Py_INCREF(nxt_py_lifespan);
+ ctx_data->lifespan = (PyObject *) lifespan;
+ Py_INCREF(ctx_data->lifespan);
- rc = NXT_OK;
+ rc = NXT_UNIT_OK;
}
release_task:
@@ -201,17 +236,21 @@ release_lifespan:
}
-nxt_int_t
-nxt_py_asgi_lifespan_shutdown(void)
+int
+nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx)
{
PyObject *msg, *future, *res;
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;
- if (nxt_slow_path(nxt_py_lifespan == NULL || nxt_py_lifespan->disabled)) {
- return NXT_OK;
+ if (nxt_slow_path(lifespan == NULL || lifespan->disabled)) {
+ return NXT_UNIT_OK;
}
- lifespan = nxt_py_lifespan;
lifespan->shutdown_called = 1;
if (lifespan->receive_future != NULL) {
@@ -231,29 +270,29 @@ nxt_py_asgi_lifespan_shutdown(void)
}
if (lifespan->shutdown_sent) {
- return NXT_OK;
+ return NXT_UNIT_OK;
}
- lifespan->shutdown_future = PyObject_CallObject(nxt_py_loop_create_future,
+ lifespan->shutdown_future = PyObject_CallObject(ctx_data->loop_create_future,
NULL);
if (nxt_slow_path(lifespan->shutdown_future == NULL)) {
nxt_unit_alert(NULL, "Python failed to create Future object");
nxt_python_print_exception();
- return NXT_ERROR;
+ return NXT_UNIT_ERROR;
}
- res = PyObject_CallFunctionObjArgs(nxt_py_loop_run_until_complete,
+ res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete,
lifespan->shutdown_future, NULL);
if (nxt_slow_path(res == NULL)) {
nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete");
nxt_python_print_exception();
- return NXT_ERROR;
+ return NXT_UNIT_ERROR;
}
Py_DECREF(res);
Py_CLEAR(lifespan->shutdown_future);
- return NXT_OK;
+ return NXT_UNIT_OK;
}
@@ -262,12 +301,14 @@ nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none)
{
PyObject *msg, *future;
nxt_py_asgi_lifespan_t *lifespan;
+ nxt_py_asgi_ctx_data_t *ctx_data;
lifespan = (nxt_py_asgi_lifespan_t *) self;
+ ctx_data = lifespan->ctx_data;
nxt_unit_debug(NULL, "asgi_lifespan_receive");
- future = PyObject_CallObject(nxt_py_loop_create_future, NULL);
+ future = PyObject_CallObject(ctx_data->loop_create_future, NULL);
if (nxt_slow_path(future == NULL)) {
nxt_unit_alert(NULL, "Python failed to create Future object");
nxt_python_print_exception();
@@ -281,7 +322,7 @@ nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none)
msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_startup_str);
- return nxt_py_asgi_set_result_soon(NULL, future, msg);
+ return nxt_py_asgi_set_result_soon(NULL, ctx_data, future, msg);
}
if (lifespan->shutdown_called && !lifespan->shutdown_received) {
@@ -289,7 +330,7 @@ nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none)
msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_shutdown_str);
- return nxt_py_asgi_set_result_soon(NULL, future, msg);
+ return nxt_py_asgi_set_result_soon(NULL, ctx_data, future, msg);
}
Py_INCREF(future);
diff --git a/src/python/nxt_python_asgi_str.c b/src/python/nxt_python_asgi_str.c
index 37fa7f04..34422973 100644
--- a/src/python/nxt_python_asgi_str.c
+++ b/src/python/nxt_python_asgi_str.c
@@ -124,7 +124,7 @@ static nxt_python_string_t nxt_py_asgi_strings[] = {
};
-nxt_int_t
+int
nxt_py_asgi_str_init(void)
{
return nxt_python_init_strings(nxt_py_asgi_strings);
diff --git a/src/python/nxt_python_asgi_str.h b/src/python/nxt_python_asgi_str.h
index 3f389c62..92969fd2 100644
--- a/src/python/nxt_python_asgi_str.h
+++ b/src/python/nxt_python_asgi_str.h
@@ -62,7 +62,7 @@ extern PyObject *nxt_py_ws_str;
extern PyObject *nxt_py_wss_str;
-nxt_int_t nxt_py_asgi_str_init(void);
+int nxt_py_asgi_str_init(void);
void nxt_py_asgi_str_done(void);
diff --git a/src/python/nxt_python_asgi_websocket.c b/src/python/nxt_python_asgi_websocket.c
index 5a27b588..fc7d9fa4 100644
--- a/src/python/nxt_python_asgi_websocket.c
+++ b/src/python/nxt_python_asgi_websocket.c
@@ -98,16 +98,16 @@ static uint64_t nxt_py_asgi_ws_max_frame_size = 1024 * 1024;
static uint64_t nxt_py_asgi_ws_max_buffer_size = 10 * 1024 * 1024;
-nxt_int_t
-nxt_py_asgi_websocket_init(nxt_task_t *task)
+int
+nxt_py_asgi_websocket_init(void)
{
if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_websocket_type) != 0)) {
- nxt_alert(task,
+ nxt_unit_alert(NULL,
"Python failed to initialize the \"asgi_websocket\" type object");
- return NXT_ERROR;
+ return NXT_UNIT_ERROR;
}
- return NXT_OK;
+ return NXT_UNIT_OK;
}
@@ -137,6 +137,7 @@ static PyObject *
nxt_py_asgi_websocket_receive(PyObject *self, PyObject *none)
{
PyObject *future, *msg;
+ nxt_py_asgi_ctx_data_t *ctx_data;
nxt_py_asgi_websocket_t *ws;
ws = (nxt_py_asgi_websocket_t *) self;
@@ -160,7 +161,9 @@ nxt_py_asgi_websocket_receive(PyObject *self, PyObject *none)
"WebSocket already closed");
}
- future = PyObject_CallObject(nxt_py_loop_create_future, NULL);
+ ctx_data = ws->req->ctx->data;
+
+ future = PyObject_CallObject(ctx_data->loop_create_future, NULL);
if (nxt_slow_path(future == NULL)) {
nxt_unit_req_alert(ws->req, "Python failed to create Future object");
nxt_python_print_exception();
@@ -174,19 +177,19 @@ nxt_py_asgi_websocket_receive(PyObject *self, PyObject *none)
msg = nxt_py_asgi_new_msg(ws->req, nxt_py_websocket_connect_str);
- return nxt_py_asgi_set_result_soon(ws->req, future, msg);
+ return nxt_py_asgi_set_result_soon(ws->req, ctx_data, future, msg);
}
if (ws->pending_fins > 0) {
msg = nxt_py_asgi_websocket_pop_msg(ws, NULL);
- return nxt_py_asgi_set_result_soon(ws->req, future, msg);
+ return nxt_py_asgi_set_result_soon(ws->req, ctx_data, future, msg);
}
if (nxt_slow_path(ws->state == NXT_WS_DISCONNECTED)) {
msg = nxt_py_asgi_websocket_disconnect_msg(ws);
- return nxt_py_asgi_set_result_soon(ws->req, future, msg);
+ return nxt_py_asgi_set_result_soon(ws->req, ctx_data, future, msg);
}
ws->receive_future = future;
diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c
index 97030cd3..da7b183c 100644
--- a/src/python/nxt_python_wsgi.c
+++ b/src/python/nxt_python_wsgi.c
@@ -38,55 +38,57 @@
*/
-typedef struct nxt_python_run_ctx_s nxt_python_run_ctx_t;
-
typedef struct {
PyObject_HEAD
-} nxt_py_input_t;
+ uint64_t content_length;
+ uint64_t bytes_sent;
+ PyObject *environ;
+ PyObject *start_resp;
+ PyObject *write;
+ nxt_unit_request_info_t *req;
+ PyThreadState *thread_state;
+} nxt_python_ctx_t;
-typedef struct {
- PyObject_HEAD
-} nxt_py_error_t;
+
+static int nxt_python_wsgi_ctx_data_alloc(void **pdata);
+static void nxt_python_wsgi_ctx_data_free(void *data);
+static int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx);
+static void nxt_python_wsgi_done(void);
static void nxt_python_request_handler(nxt_unit_request_info_t *req);
-static PyObject *nxt_python_create_environ(nxt_task_t *task);
-static PyObject *nxt_python_get_environ(nxt_python_run_ctx_t *ctx);
-static int nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, PyObject *name,
+static PyObject *nxt_python_create_environ(nxt_python_app_conf_t *c);
+static PyObject *nxt_python_get_environ(nxt_python_ctx_t *pctx);
+static int nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name,
nxt_unit_sptr_t *sptr, uint32_t size);
-static int nxt_python_add_field(nxt_python_run_ctx_t *ctx,
+static int nxt_python_add_field(nxt_python_ctx_t *pctx,
nxt_unit_field_t *field, int n, uint32_t vl);
static PyObject *nxt_python_field_name(const char *name, uint8_t len);
static PyObject *nxt_python_field_value(nxt_unit_field_t *f, int n,
uint32_t vl);
-static int nxt_python_add_obj(nxt_python_run_ctx_t *ctx, PyObject *name,
+static int nxt_python_add_obj(nxt_python_ctx_t *pctx, PyObject *name,
PyObject *value);
static PyObject *nxt_py_start_resp(PyObject *self, PyObject *args);
-static int nxt_python_response_add_field(nxt_python_run_ctx_t *ctx,
+static int nxt_python_response_add_field(nxt_python_ctx_t *pctx,
PyObject *name, PyObject *value, int i);
static int nxt_python_str_buf(PyObject *str, char **buf, uint32_t *len,
PyObject **bytes);
static PyObject *nxt_py_write(PyObject *self, PyObject *args);
-static void nxt_py_input_dealloc(nxt_py_input_t *self);
-static PyObject *nxt_py_input_read(nxt_py_input_t *self, PyObject *args);
-static PyObject *nxt_py_input_readline(nxt_py_input_t *self, PyObject *args);
-static PyObject *nxt_py_input_getline(nxt_python_run_ctx_t *ctx, size_t size);
-static PyObject *nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args);
+static void nxt_py_input_dealloc(nxt_python_ctx_t *pctx);
+static PyObject *nxt_py_input_read(nxt_python_ctx_t *pctx, PyObject *args);
+static PyObject *nxt_py_input_readline(nxt_python_ctx_t *pctx,
+ PyObject *args);
+static PyObject *nxt_py_input_getline(nxt_python_ctx_t *pctx, size_t size);
+static PyObject *nxt_py_input_readlines(nxt_python_ctx_t *self,
+ PyObject *args);
-static PyObject *nxt_py_input_iter(PyObject *self);
-static PyObject *nxt_py_input_next(PyObject *self);
+static PyObject *nxt_py_input_iter(PyObject *pctx);
+static PyObject *nxt_py_input_next(PyObject *pctx);
-static int nxt_python_write(nxt_python_run_ctx_t *ctx, PyObject *bytes);
-
-struct nxt_python_run_ctx_s {
- uint64_t content_length;
- uint64_t bytes_sent;
- PyObject *environ;
- nxt_unit_request_info_t *req;
-};
+static int nxt_python_write(nxt_python_ctx_t *pctx, PyObject *bytes);
static PyMethodDef nxt_py_start_resp_method[] = {
@@ -111,7 +113,7 @@ static PyTypeObject nxt_py_input_type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "unit._input",
- .tp_basicsize = sizeof(nxt_py_input_t),
+ .tp_basicsize = sizeof(nxt_python_ctx_t),
.tp_dealloc = (destructor) nxt_py_input_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "unit input object.",
@@ -121,12 +123,7 @@ static PyTypeObject nxt_py_input_type = {
};
-static PyObject *nxt_py_start_resp_obj;
-static PyObject *nxt_py_write_obj;
-static PyObject *nxt_py_environ_ptyp;
-
-static PyThreadState *nxt_python_thread_state;
-static nxt_python_run_ctx_t *nxt_python_run_ctx;
+static PyObject *nxt_py_environ_ptyp;
static PyObject *nxt_py_80_str;
static PyObject *nxt_py_close_str;
@@ -143,6 +140,7 @@ static PyObject *nxt_py_server_addr_str;
static PyObject *nxt_py_server_name_str;
static PyObject *nxt_py_server_port_str;
static PyObject *nxt_py_server_protocol_str;
+static PyObject *nxt_py_wsgi_input_str;
static PyObject *nxt_py_wsgi_uri_scheme_str;
static nxt_python_string_t nxt_python_strings[] = {
@@ -161,82 +159,132 @@ static nxt_python_string_t nxt_python_strings[] = {
{ nxt_string("SERVER_NAME"), &nxt_py_server_name_str },
{ nxt_string("SERVER_PORT"), &nxt_py_server_port_str },
{ nxt_string("SERVER_PROTOCOL"), &nxt_py_server_protocol_str },
+ { nxt_string("wsgi.input"), &nxt_py_wsgi_input_str },
{ nxt_string("wsgi.url_scheme"), &nxt_py_wsgi_uri_scheme_str },
{ nxt_null_string, NULL },
};
+static nxt_python_proto_t nxt_py_wsgi_proto = {
+ .ctx_data_alloc = nxt_python_wsgi_ctx_data_alloc,
+ .ctx_data_free = nxt_python_wsgi_ctx_data_free,
+ .run = nxt_python_wsgi_run,
+ .done = nxt_python_wsgi_done,
+};
+
-nxt_int_t
-nxt_python_wsgi_init(nxt_task_t *task, nxt_unit_init_t *init)
+int
+nxt_python_wsgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
{
PyObject *obj;
obj = NULL;
- if (nxt_slow_path(nxt_python_init_strings(nxt_python_strings) != NXT_OK)) {
- nxt_alert(task, "Python failed to init string objects");
+ if (nxt_slow_path(nxt_python_init_strings(nxt_python_strings)
+ != NXT_UNIT_OK))
+ {
+ nxt_unit_alert(NULL, "Python failed to init string objects");
goto fail;
}
- obj = PyCFunction_New(nxt_py_start_resp_method, NULL);
+ obj = nxt_python_create_environ(init->data);
if (nxt_slow_path(obj == NULL)) {
- nxt_alert(task,
- "Python failed to initialize the \"start_response\" function");
goto fail;
}
- nxt_py_start_resp_obj = obj;
+ nxt_py_environ_ptyp = obj;
+ obj = NULL;
+
+ init->callbacks.request_handler = nxt_python_request_handler;
+
+ *proto = nxt_py_wsgi_proto;
- obj = PyCFunction_New(nxt_py_write_method, NULL);
- if (nxt_slow_path(obj == NULL)) {
- nxt_alert(task, "Python failed to initialize the \"write\" function");
- goto fail;
+ return NXT_UNIT_OK;
+
+fail:
+
+ Py_XDECREF(obj);
+
+ return NXT_UNIT_ERROR;
+}
+
+
+static int
+nxt_python_wsgi_ctx_data_alloc(void **pdata)
+{
+ nxt_python_ctx_t *pctx;
+
+ pctx = PyObject_New(nxt_python_ctx_t, &nxt_py_input_type);
+ if (nxt_slow_path(pctx == NULL)) {
+ nxt_unit_alert(NULL,
+ "Python failed to create the \"wsgi.input\" object");
+ return NXT_UNIT_ERROR;
}
- nxt_py_write_obj = obj;
+ pctx->write = NULL;
- obj = nxt_python_create_environ(task);
- if (nxt_slow_path(obj == NULL)) {
+ pctx->start_resp = PyCFunction_New(nxt_py_start_resp_method,
+ (PyObject *) pctx);
+ if (nxt_slow_path(pctx->start_resp == NULL)) {
+ nxt_unit_alert(NULL,
+ "Python failed to initialize the \"start_response\" function");
goto fail;
}
- nxt_py_environ_ptyp = obj;
- obj = NULL;
+ pctx->write = PyCFunction_New(nxt_py_write_method, (PyObject *) pctx);
+ if (nxt_slow_path(pctx->write == NULL)) {
+ nxt_unit_alert(NULL,
+ "Python failed to initialize the \"write\" function");
+ goto fail;
+ }
- init->callbacks.request_handler = nxt_python_request_handler;
+ *pdata = pctx;
- return NXT_OK;
+ return NXT_UNIT_OK;
fail:
- Py_XDECREF(obj);
+ nxt_python_wsgi_ctx_data_free(pctx);
- return NXT_ERROR;
+ return NXT_UNIT_ERROR;
}
-int
+static void
+nxt_python_wsgi_ctx_data_free(void *data)
+{
+ nxt_python_ctx_t *pctx;
+
+ pctx = data;
+
+ Py_XDECREF(pctx->start_resp);
+ Py_XDECREF(pctx->write);
+ Py_XDECREF(pctx);
+}
+
+
+static int
nxt_python_wsgi_run(nxt_unit_ctx_t *ctx)
{
- int rc;
+ int rc;
+ nxt_python_ctx_t *pctx;
- nxt_python_thread_state = PyEval_SaveThread();
+ pctx = ctx->data;
+
+ pctx->thread_state = PyEval_SaveThread();
rc = nxt_unit_run(ctx);
- PyEval_RestoreThread(nxt_python_thread_state);
+ PyEval_RestoreThread(pctx->thread_state);
return rc;
}
-void
+static void
nxt_python_wsgi_done(void)
{
nxt_python_done_strings(nxt_python_strings);
- Py_XDECREF(nxt_py_start_resp_obj);
- Py_XDECREF(nxt_py_write_obj);
Py_XDECREF(nxt_py_environ_ptyp);
}
@@ -244,14 +292,20 @@ nxt_python_wsgi_done(void)
static void
nxt_python_request_handler(nxt_unit_request_info_t *req)
{
- int rc;
- PyObject *environ, *args, *response, *iterator, *item;
- PyObject *close, *result;
- nxt_python_run_ctx_t run_ctx = {-1, 0, NULL, req};
+ int rc;
+ PyObject *environ, *args, *response, *iterator, *item;
+ PyObject *close, *result;
+ nxt_python_ctx_t *pctx;
- PyEval_RestoreThread(nxt_python_thread_state);
+ pctx = req->ctx->data;
- environ = nxt_python_get_environ(&run_ctx);
+ pctx->content_length = -1;
+ pctx->bytes_sent = 0;
+ pctx->req = req;
+
+ PyEval_RestoreThread(pctx->thread_state);
+
+ environ = nxt_python_get_environ(pctx);
if (nxt_slow_path(environ == NULL)) {
rc = NXT_UNIT_ERROR;
goto done;
@@ -269,10 +323,8 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
PyTuple_SET_ITEM(args, 0, environ);
- Py_INCREF(nxt_py_start_resp_obj);
- PyTuple_SET_ITEM(args, 1, nxt_py_start_resp_obj);
-
- nxt_python_run_ctx = &run_ctx;
+ Py_INCREF(pctx->start_resp);
+ PyTuple_SET_ITEM(args, 1, pctx->start_resp);
response = PyObject_CallObject(nxt_py_application, args);
@@ -288,7 +340,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
/* Shortcut: avoid iterate over response string symbols. */
if (PyBytes_Check(response)) {
- rc = nxt_python_write(&run_ctx, response);
+ rc = nxt_python_write(pctx, response);
} else {
iterator = PyObject_GetIter(response);
@@ -296,7 +348,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
if (nxt_fast_path(iterator != NULL)) {
rc = NXT_UNIT_OK;
- while (run_ctx.bytes_sent < run_ctx.content_length) {
+ while (pctx->bytes_sent < pctx->content_length) {
item = PyIter_Next(iterator);
if (item == NULL) {
@@ -312,7 +364,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
}
if (nxt_fast_path(PyBytes_Check(item))) {
- rc = nxt_python_write(&run_ctx, item);
+ rc = nxt_python_write(pctx, item);
} else {
nxt_unit_req_error(req, "the application returned "
@@ -361,29 +413,31 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
done:
- nxt_python_thread_state = PyEval_SaveThread();
+ pctx->thread_state = PyEval_SaveThread();
+
+ pctx->req = NULL;
- nxt_python_run_ctx = NULL;
nxt_unit_request_done(req, rc);
}
static PyObject *
-nxt_python_create_environ(nxt_task_t *task)
+nxt_python_create_environ(nxt_python_app_conf_t *c)
{
PyObject *obj, *err, *environ;
environ = PyDict_New();
if (nxt_slow_path(environ == NULL)) {
- nxt_alert(task, "Python failed to create the \"environ\" dictionary");
+ nxt_unit_alert(NULL,
+ "Python failed to create the \"environ\" dictionary");
return NULL;
}
obj = PyString_FromStringAndSize((char *) nxt_server.start,
nxt_server.length);
if (nxt_slow_path(obj == NULL)) {
- nxt_alert(task,
+ nxt_unit_alert(NULL,
"Python failed to create the \"SERVER_SOFTWARE\" environ value");
goto fail;
}
@@ -391,7 +445,7 @@ nxt_python_create_environ(nxt_task_t *task)
if (nxt_slow_path(PyDict_SetItemString(environ, "SERVER_SOFTWARE", obj)
!= 0))
{
- nxt_alert(task,
+ nxt_unit_alert(NULL,
"Python failed to set the \"SERVER_SOFTWARE\" environ value");
goto fail;
}
@@ -401,15 +455,15 @@ nxt_python_create_environ(nxt_task_t *task)
obj = Py_BuildValue("(ii)", 1, 0);
if (nxt_slow_path(obj == NULL)) {
- nxt_alert(task,
+ nxt_unit_alert(NULL,
"Python failed to build the \"wsgi.version\" environ value");
goto fail;
}
if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.version", obj) != 0))
{
- nxt_alert(task,
- "Python failed to set the \"wsgi.version\" environ value");
+ nxt_unit_alert(NULL,
+ "Python failed to set the \"wsgi.version\" environ value");
goto fail;
}
@@ -418,10 +472,10 @@ nxt_python_create_environ(nxt_task_t *task)
if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.multithread",
- Py_False)
+ c->threads > 1 ? Py_True : Py_False)
!= 0))
{
- nxt_alert(task,
+ nxt_unit_alert(NULL,
"Python failed to set the \"wsgi.multithread\" environ value");
goto fail;
}
@@ -430,7 +484,7 @@ nxt_python_create_environ(nxt_task_t *task)
Py_True)
!= 0))
{
- nxt_alert(task,
+ nxt_unit_alert(NULL,
"Python failed to set the \"wsgi.multiprocess\" environ value");
goto fail;
}
@@ -439,46 +493,30 @@ nxt_python_create_environ(nxt_task_t *task)
Py_False)
!= 0))
{
- nxt_alert(task,
+ nxt_unit_alert(NULL,
"Python failed to set the \"wsgi.run_once\" environ value");
goto fail;
}
if (nxt_slow_path(PyType_Ready(&nxt_py_input_type) != 0)) {
- nxt_alert(task,
+ nxt_unit_alert(NULL,
"Python failed to initialize the \"wsgi.input\" type object");
goto fail;
}
- obj = (PyObject *) PyObject_New(nxt_py_input_t, &nxt_py_input_type);
-
- if (nxt_slow_path(obj == NULL)) {
- nxt_alert(task, "Python failed to create the \"wsgi.input\" object");
- goto fail;
- }
-
- if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.input", obj) != 0)) {
- nxt_alert(task,
- "Python failed to set the \"wsgi.input\" environ value");
- goto fail;
- }
-
- Py_DECREF(obj);
- obj = NULL;
-
err = PySys_GetObject((char *) "stderr");
if (nxt_slow_path(err == NULL)) {
- nxt_alert(task, "Python failed to get \"sys.stderr\" object");
+ nxt_unit_alert(NULL, "Python failed to get \"sys.stderr\" object");
goto fail;
}
if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.errors", err) != 0))
{
- nxt_alert(task,
- "Python failed to set the \"wsgi.errors\" environ value");
+ nxt_unit_alert(NULL,
+ "Python failed to set the \"wsgi.errors\" environ value");
goto fail;
}
@@ -494,7 +532,7 @@ fail:
static PyObject *
-nxt_python_get_environ(nxt_python_run_ctx_t *ctx)
+nxt_python_get_environ(nxt_python_ctx_t *pctx)
{
int rc;
uint32_t i, j, vl;
@@ -504,15 +542,15 @@ nxt_python_get_environ(nxt_python_run_ctx_t *ctx)
environ = PyDict_Copy(nxt_py_environ_ptyp);
if (nxt_slow_path(environ == NULL)) {
- nxt_unit_req_error(ctx->req,
+ nxt_unit_req_error(pctx->req,
"Python failed to copy the \"environ\" dictionary");
return NULL;
}
- ctx->environ = environ;
+ pctx->environ = environ;
- r = ctx->req->request;
+ r = pctx->req->request;
#define RC(S) \
do { \
@@ -522,36 +560,36 @@ nxt_python_get_environ(nxt_python_run_ctx_t *ctx)
} \
} while(0)
- RC(nxt_python_add_sptr(ctx, nxt_py_request_method_str, &r->method,
+ RC(nxt_python_add_sptr(pctx, nxt_py_request_method_str, &r->method,
r->method_length));
- RC(nxt_python_add_sptr(ctx, nxt_py_request_uri_str, &r->target,
+ RC(nxt_python_add_sptr(pctx, nxt_py_request_uri_str, &r->target,
r->target_length));
- RC(nxt_python_add_sptr(ctx, nxt_py_query_string_str, &r->query,
+ RC(nxt_python_add_sptr(pctx, nxt_py_query_string_str, &r->query,
r->query_length));
- RC(nxt_python_add_sptr(ctx, nxt_py_path_info_str, &r->path,
+ RC(nxt_python_add_sptr(pctx, nxt_py_path_info_str, &r->path,
r->path_length));
- RC(nxt_python_add_sptr(ctx, nxt_py_remote_addr_str, &r->remote,
+ RC(nxt_python_add_sptr(pctx, nxt_py_remote_addr_str, &r->remote,
r->remote_length));
- RC(nxt_python_add_sptr(ctx, nxt_py_server_addr_str, &r->local,
+ RC(nxt_python_add_sptr(pctx, nxt_py_server_addr_str, &r->local,
r->local_length));
if (r->tls) {
- RC(nxt_python_add_obj(ctx, nxt_py_wsgi_uri_scheme_str,
+ RC(nxt_python_add_obj(pctx, nxt_py_wsgi_uri_scheme_str,
nxt_py_https_str));
} else {
- RC(nxt_python_add_obj(ctx, nxt_py_wsgi_uri_scheme_str,
+ RC(nxt_python_add_obj(pctx, nxt_py_wsgi_uri_scheme_str,
nxt_py_http_str));
}
- RC(nxt_python_add_sptr(ctx, nxt_py_server_protocol_str, &r->version,
+ RC(nxt_python_add_sptr(pctx, nxt_py_server_protocol_str, &r->version,
r->version_length));
- RC(nxt_python_add_sptr(ctx, nxt_py_server_name_str, &r->server_name,
+ RC(nxt_python_add_sptr(pctx, nxt_py_server_name_str, &r->server_name,
r->server_name_length));
- RC(nxt_python_add_obj(ctx, nxt_py_server_port_str, nxt_py_80_str));
+ RC(nxt_python_add_obj(pctx, nxt_py_server_port_str, nxt_py_80_str));
- nxt_unit_request_group_dup_fields(ctx->req);
+ nxt_unit_request_group_dup_fields(pctx->req);
for (i = 0; i < r->fields_count;) {
f = r->fields + i;
@@ -569,7 +607,7 @@ nxt_python_get_environ(nxt_python_run_ctx_t *ctx)
vl += 2 + f2->value_length;
}
- RC(nxt_python_add_field(ctx, f, j - i, vl));
+ RC(nxt_python_add_field(pctx, f, j - i, vl));
i = j;
}
@@ -577,19 +615,27 @@ nxt_python_get_environ(nxt_python_run_ctx_t *ctx)
if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
f = r->fields + r->content_length_field;
- RC(nxt_python_add_sptr(ctx, nxt_py_content_length_str, &f->value,
+ RC(nxt_python_add_sptr(pctx, nxt_py_content_length_str, &f->value,
f->value_length));
}
if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
f = r->fields + r->content_type_field;
- RC(nxt_python_add_sptr(ctx, nxt_py_content_type_str, &f->value,
+ RC(nxt_python_add_sptr(pctx, nxt_py_content_type_str, &f->value,
f->value_length));
}
#undef RC
+ if (nxt_slow_path(PyDict_SetItem(environ, nxt_py_wsgi_input_str,
+ (PyObject *) pctx) != 0))
+ {
+ nxt_unit_req_error(pctx->req,
+ "Python failed to set the \"wsgi.input\" environ value");
+ goto fail;
+ }
+
return environ;
fail:
@@ -601,7 +647,7 @@ fail:
static int
-nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, PyObject *name,
+nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name,
nxt_unit_sptr_t *sptr, uint32_t size)
{
char *src;
@@ -611,7 +657,7 @@ nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, PyObject *name,
value = PyString_FromStringAndSize(src, size);
if (nxt_slow_path(value == NULL)) {
- nxt_unit_req_error(ctx->req,
+ nxt_unit_req_error(pctx->req,
"Python failed to create value string \"%.*s\"",
(int) size, src);
nxt_python_print_exception();
@@ -619,8 +665,8 @@ nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, PyObject *name,
return NXT_UNIT_ERROR;
}
- if (nxt_slow_path(PyDict_SetItem(ctx->environ, name, value) != 0)) {
- nxt_unit_req_error(ctx->req,
+ if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) {
+ nxt_unit_req_error(pctx->req,
"Python failed to set the \"%s\" environ value",
PyUnicode_AsUTF8(name));
Py_DECREF(value);
@@ -635,7 +681,7 @@ nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, PyObject *name,
static int
-nxt_python_add_field(nxt_python_run_ctx_t *ctx, nxt_unit_field_t *field, int n,
+nxt_python_add_field(nxt_python_ctx_t *pctx, nxt_unit_field_t *field, int n,
uint32_t vl)
{
char *src;
@@ -645,7 +691,7 @@ nxt_python_add_field(nxt_python_run_ctx_t *ctx, nxt_unit_field_t *field, int n,
name = nxt_python_field_name(src, field->name_length);
if (nxt_slow_path(name == NULL)) {
- nxt_unit_req_error(ctx->req,
+ nxt_unit_req_error(pctx->req,
"Python failed to create name string \"%.*s\"",
(int) field->name_length, src);
nxt_python_print_exception();
@@ -656,7 +702,7 @@ nxt_python_add_field(nxt_python_run_ctx_t *ctx, nxt_unit_field_t *field, int n,
value = nxt_python_field_value(field, n, vl);
if (nxt_slow_path(value == NULL)) {
- nxt_unit_req_error(ctx->req,
+ nxt_unit_req_error(pctx->req,
"Python failed to create value string \"%.*s\"",
(int) field->value_length,
(char *) nxt_unit_sptr_get(&field->value));
@@ -665,8 +711,8 @@ nxt_python_add_field(nxt_python_run_ctx_t *ctx, nxt_unit_field_t *field, int n,
goto fail;
}
- if (nxt_slow_path(PyDict_SetItem(ctx->environ, name, value) != 0)) {
- nxt_unit_req_error(ctx->req,
+ if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) {
+ nxt_unit_req_error(pctx->req,
"Python failed to set the \"%s\" environ value",
PyUnicode_AsUTF8(name));
goto fail;
@@ -761,10 +807,10 @@ nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl)
static int
-nxt_python_add_obj(nxt_python_run_ctx_t *ctx, PyObject *name, PyObject *value)
+nxt_python_add_obj(nxt_python_ctx_t *pctx, PyObject *name, PyObject *value)
{
- if (nxt_slow_path(PyDict_SetItem(ctx->environ, name, value) != 0)) {
- nxt_unit_req_error(ctx->req,
+ if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) {
+ nxt_unit_req_error(pctx->req,
"Python failed to set the \"%s\" environ value",
PyUnicode_AsUTF8(name));
@@ -778,15 +824,15 @@ nxt_python_add_obj(nxt_python_run_ctx_t *ctx, PyObject *name, PyObject *value)
static PyObject *
nxt_py_start_resp(PyObject *self, PyObject *args)
{
- int rc, status;
- char *status_str, *space_ptr;
- uint32_t status_len;
- PyObject *headers, *tuple, *string, *status_bytes;
- Py_ssize_t i, n, fields_size, fields_count;
- nxt_python_run_ctx_t *ctx;
-
- ctx = nxt_python_run_ctx;
- if (nxt_slow_path(ctx == NULL)) {
+ int rc, status;
+ char *status_str, *space_ptr;
+ uint32_t status_len;
+ PyObject *headers, *tuple, *string, *status_bytes;
+ Py_ssize_t i, n, fields_size, fields_count;
+ nxt_python_ctx_t *pctx;
+
+ pctx = (nxt_python_ctx_t *) self;
+ if (nxt_slow_path(pctx->req == NULL)) {
return PyErr_Format(PyExc_RuntimeError,
"start_response() is called "
"outside of WSGI request processing");
@@ -831,7 +877,7 @@ nxt_py_start_resp(PyObject *self, PyObject *args)
fields_size += PyBytes_GET_SIZE(string);
} else if (PyUnicode_Check(string)) {
- fields_size += PyUnicode_GET_SIZE(string);
+ fields_size += PyUnicode_GET_LENGTH(string);
} else {
return PyErr_Format(PyExc_TypeError,
@@ -843,7 +889,7 @@ nxt_py_start_resp(PyObject *self, PyObject *args)
fields_size += PyBytes_GET_SIZE(string);
} else if (PyUnicode_Check(string)) {
- fields_size += PyUnicode_GET_SIZE(string);
+ fields_size += PyUnicode_GET_LENGTH(string);
} else {
return PyErr_Format(PyExc_TypeError,
@@ -851,7 +897,7 @@ nxt_py_start_resp(PyObject *self, PyObject *args)
}
}
- ctx->content_length = -1;
+ pctx->content_length = -1;
string = PyTuple_GET_ITEM(args, 0);
rc = nxt_python_str_buf(string, &status_str, &status_len, &status_bytes);
@@ -877,7 +923,7 @@ nxt_py_start_resp(PyObject *self, PyObject *args)
* ... applications can replace their originally intended output with error
* output, up until the last possible moment.
*/
- rc = nxt_unit_response_init(ctx->req, status, fields_count, fields_size);
+ rc = nxt_unit_response_init(pctx->req, status, fields_count, fields_size);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
return PyErr_Format(PyExc_RuntimeError,
"failed to allocate response object");
@@ -886,7 +932,7 @@ nxt_py_start_resp(PyObject *self, PyObject *args)
for (i = 0; i < fields_count; i++) {
tuple = PyList_GET_ITEM(headers, i);
- rc = nxt_python_response_add_field(ctx, PyTuple_GET_ITEM(tuple, 0),
+ rc = nxt_python_response_add_field(pctx, PyTuple_GET_ITEM(tuple, 0),
PyTuple_GET_ITEM(tuple, 1), i);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
return PyErr_Format(PyExc_RuntimeError,
@@ -907,21 +953,21 @@ nxt_py_start_resp(PyObject *self, PyObject *args)
* possible exception to this rule is if the response headers explicitly
* include a Content-Length of zero.)
*/
- if (ctx->content_length == 0) {
- rc = nxt_unit_response_send(ctx->req);
+ if (pctx->content_length == 0) {
+ rc = nxt_unit_response_send(pctx->req);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
return PyErr_Format(PyExc_RuntimeError,
"failed to send response headers");
}
}
- Py_INCREF(nxt_py_write_obj);
- return nxt_py_write_obj;
+ Py_INCREF(pctx->write);
+ return pctx->write;
}
static int
-nxt_python_response_add_field(nxt_python_run_ctx_t *ctx, PyObject *name,
+nxt_python_response_add_field(nxt_python_ctx_t *pctx, PyObject *name,
PyObject *value, int i)
{
int rc;
@@ -943,20 +989,20 @@ nxt_python_response_add_field(nxt_python_run_ctx_t *ctx, PyObject *name,
goto fail;
}
- rc = nxt_unit_response_add_field(ctx->req, name_str, name_length,
+ rc = nxt_unit_response_add_field(pctx->req, name_str, name_length,
value_str, value_length);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
goto fail;
}
- if (ctx->req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) {
+ if (pctx->req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) {
content_length = nxt_off_t_parse((u_char *) value_str, value_length);
if (nxt_slow_path(content_length < 0)) {
- nxt_unit_req_error(ctx->req, "failed to parse Content-Length "
+ nxt_unit_req_error(pctx->req, "failed to parse Content-Length "
"value %.*s", (int) value_length, value_str);
} else {
- ctx->content_length = content_length;
+ pctx->content_length = content_length;
}
}
@@ -1001,7 +1047,7 @@ nxt_py_write(PyObject *self, PyObject *str)
NXT_PYTHON_BYTES_TYPE);
}
- rc = nxt_python_write(nxt_python_run_ctx, str);
+ rc = nxt_python_write((nxt_python_ctx_t *) self, str);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
return PyErr_Format(PyExc_RuntimeError,
"failed to write response value");
@@ -1012,28 +1058,26 @@ nxt_py_write(PyObject *self, PyObject *str)
static void
-nxt_py_input_dealloc(nxt_py_input_t *self)
+nxt_py_input_dealloc(nxt_python_ctx_t *self)
{
PyObject_Del(self);
}
static PyObject *
-nxt_py_input_read(nxt_py_input_t *self, PyObject *args)
+nxt_py_input_read(nxt_python_ctx_t *pctx, PyObject *args)
{
- char *buf;
- PyObject *content, *obj;
- Py_ssize_t size, n;
- nxt_python_run_ctx_t *ctx;
+ char *buf;
+ PyObject *content, *obj;
+ Py_ssize_t size, n;
- ctx = nxt_python_run_ctx;
- if (nxt_slow_path(ctx == NULL)) {
+ if (nxt_slow_path(pctx->req == NULL)) {
return PyErr_Format(PyExc_RuntimeError,
"wsgi.input.read() is called "
"outside of WSGI request processing");
}
- size = ctx->req->content_length;
+ size = pctx->req->content_length;
n = PyTuple_GET_SIZE(args);
@@ -1057,8 +1101,8 @@ nxt_py_input_read(nxt_py_input_t *self, PyObject *args)
}
}
- if (size == -1 || size > (Py_ssize_t) ctx->req->content_length) {
- size = ctx->req->content_length;
+ if (size == -1 || size > (Py_ssize_t) pctx->req->content_length) {
+ size = pctx->req->content_length;
}
}
@@ -1069,22 +1113,20 @@ nxt_py_input_read(nxt_py_input_t *self, PyObject *args)
buf = PyBytes_AS_STRING(content);
- size = nxt_unit_request_read(ctx->req, buf, size);
+ size = nxt_unit_request_read(pctx->req, buf, size);
return content;
}
static PyObject *
-nxt_py_input_readline(nxt_py_input_t *self, PyObject *args)
+nxt_py_input_readline(nxt_python_ctx_t *pctx, PyObject *args)
{
- ssize_t ssize;
- PyObject *obj;
- Py_ssize_t n;
- nxt_python_run_ctx_t *ctx;
+ ssize_t ssize;
+ PyObject *obj;
+ Py_ssize_t n;
- ctx = nxt_python_run_ctx;
- if (nxt_slow_path(ctx == NULL)) {
+ if (nxt_slow_path(pctx->req == NULL)) {
return PyErr_Format(PyExc_RuntimeError,
"wsgi.input.readline() is called "
"outside of WSGI request processing");
@@ -1102,7 +1144,7 @@ nxt_py_input_readline(nxt_py_input_t *self, PyObject *args)
ssize = PyNumber_AsSsize_t(obj, PyExc_OverflowError);
if (nxt_fast_path(ssize > 0)) {
- return nxt_py_input_getline(ctx, ssize);
+ return nxt_py_input_getline(pctx, ssize);
}
if (ssize == 0) {
@@ -1119,18 +1161,18 @@ nxt_py_input_readline(nxt_py_input_t *self, PyObject *args)
}
}
- return nxt_py_input_getline(ctx, SSIZE_MAX);
+ return nxt_py_input_getline(pctx, SSIZE_MAX);
}
static PyObject *
-nxt_py_input_getline(nxt_python_run_ctx_t *ctx, size_t size)
+nxt_py_input_getline(nxt_python_ctx_t *pctx, size_t size)
{
void *buf;
ssize_t res;
PyObject *content;
- res = nxt_unit_request_readline_size(ctx->req, size);
+ res = nxt_unit_request_readline_size(pctx->req, size);
if (nxt_slow_path(res < 0)) {
return NULL;
}
@@ -1146,20 +1188,18 @@ nxt_py_input_getline(nxt_python_run_ctx_t *ctx, size_t size)
buf = PyBytes_AS_STRING(content);
- res = nxt_unit_request_read(ctx->req, buf, res);
+ res = nxt_unit_request_read(pctx->req, buf, res);
return content;
}
static PyObject *
-nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args)
+nxt_py_input_readlines(nxt_python_ctx_t *pctx, PyObject *args)
{
- PyObject *res;
- nxt_python_run_ctx_t *ctx;
+ PyObject *res;
- ctx = nxt_python_run_ctx;
- if (nxt_slow_path(ctx == NULL)) {
+ if (nxt_slow_path(pctx->req == NULL)) {
return PyErr_Format(PyExc_RuntimeError,
"wsgi.input.readlines() is called "
"outside of WSGI request processing");
@@ -1171,7 +1211,7 @@ nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args)
}
for ( ;; ) {
- PyObject *line = nxt_py_input_getline(ctx, SSIZE_MAX);
+ PyObject *line = nxt_py_input_getline(pctx, SSIZE_MAX);
if (nxt_slow_path(line == NULL)) {
Py_DECREF(res);
return NULL;
@@ -1201,17 +1241,17 @@ nxt_py_input_iter(PyObject *self)
static PyObject *
nxt_py_input_next(PyObject *self)
{
- PyObject *line;
- nxt_python_run_ctx_t *ctx;
+ PyObject *line;
+ nxt_python_ctx_t *pctx;
- ctx = nxt_python_run_ctx;
- if (nxt_slow_path(ctx == NULL)) {
+ pctx = (nxt_python_ctx_t *) self;
+ if (nxt_slow_path(pctx->req == NULL)) {
return PyErr_Format(PyExc_RuntimeError,
"wsgi.input.next() is called "
"outside of WSGI request processing");
}
- line = nxt_py_input_getline(ctx, SSIZE_MAX);
+ line = nxt_py_input_getline(pctx, SSIZE_MAX);
if (nxt_slow_path(line == NULL)) {
return NULL;
}
@@ -1227,7 +1267,7 @@ nxt_py_input_next(PyObject *self)
static int
-nxt_python_write(nxt_python_run_ctx_t *ctx, PyObject *bytes)
+nxt_python_write(nxt_python_ctx_t *pctx, PyObject *bytes)
{
int rc;
char *str_buf;
@@ -1248,16 +1288,16 @@ nxt_python_write(nxt_python_run_ctx_t *ctx, PyObject *bytes)
* stop iterating over the response when enough data has been sent, or raise
* an error if the application tries to write() past that point.
*/
- if (nxt_slow_path(str_length > ctx->content_length - ctx->bytes_sent)) {
- nxt_unit_req_error(ctx->req, "content length %"PRIu64" exceeded",
- ctx->content_length);
+ if (nxt_slow_path(str_length > pctx->content_length - pctx->bytes_sent)) {
+ nxt_unit_req_error(pctx->req, "content length %"PRIu64" exceeded",
+ pctx->content_length);
return NXT_UNIT_ERROR;
}
- rc = nxt_unit_response_write(ctx->req, str_buf, str_length);
+ rc = nxt_unit_response_write(pctx->req, str_buf, str_length);
if (nxt_fast_path(rc == NXT_UNIT_OK)) {
- ctx->bytes_sent += str_length;
+ pctx->bytes_sent += str_length;
}
return rc;
diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c
index 743bf646..698d4a43 100644
--- a/src/ruby/nxt_ruby.c
+++ b/src/ruby/nxt_ruby.c
@@ -8,32 +8,25 @@
#include <nxt_unit.h>
#include <nxt_unit_request.h>
+#include <ruby/thread.h>
+
#include NXT_RUBY_MOUNTS_H
#define NXT_RUBY_RACK_API_VERSION_MAJOR 1
#define NXT_RUBY_RACK_API_VERSION_MINOR 3
-#define NXT_RUBY_STRINGIZE_HELPER(x) #x
-#define NXT_RUBY_STRINGIZE(x) NXT_RUBY_STRINGIZE_HELPER(x)
-
-#define NXT_RUBY_LIB_VERSION \
- NXT_RUBY_STRINGIZE(RUBY_API_VERSION_MAJOR) \
- "." NXT_RUBY_STRINGIZE(RUBY_API_VERSION_MINOR) \
- "." NXT_RUBY_STRINGIZE(RUBY_API_VERSION_TEENY)
-
typedef struct {
- nxt_task_t *task;
- nxt_str_t *script;
- VALUE builder;
+ nxt_task_t *task;
+ nxt_str_t *script;
+ nxt_ruby_ctx_t *rctx;
} nxt_ruby_rack_init_t;
static nxt_int_t nxt_ruby_start(nxt_task_t *task,
nxt_process_data_t *data);
static VALUE nxt_ruby_init_basic(VALUE arg);
-static nxt_int_t nxt_ruby_init_io(nxt_task_t *task);
static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init);
static VALUE nxt_ruby_require_rubygems(VALUE arg);
@@ -41,26 +34,40 @@ static VALUE nxt_ruby_bundler_setup(VALUE arg);
static VALUE nxt_ruby_require_rack(VALUE arg);
static VALUE nxt_ruby_rack_parse_script(VALUE ctx);
static VALUE nxt_ruby_rack_env_create(VALUE arg);
+static int nxt_ruby_init_io(nxt_ruby_ctx_t *rctx);
static void nxt_ruby_request_handler(nxt_unit_request_info_t *req);
+static void *nxt_ruby_request_handler_gvl(void *req);
+static int nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx);
+static VALUE nxt_ruby_thread_func(VALUE arg);
+static void *nxt_ruby_unit_run(void *ctx);
+static void nxt_ruby_ubf(void *ctx);
+static int nxt_ruby_init_threads(nxt_ruby_app_conf_t *c);
+static void nxt_ruby_join_threads(nxt_unit_ctx_t *ctx,
+ nxt_ruby_app_conf_t *c);
static VALUE nxt_ruby_rack_app_run(VALUE arg);
-static int nxt_ruby_read_request(VALUE hash_env);
-nxt_inline void nxt_ruby_add_sptr(VALUE hash_env,
- const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len);
-nxt_inline void nxt_ruby_add_str(VALUE hash_env,
- const char *name, uint32_t name_len, const char *str, uint32_t len);
-static nxt_int_t nxt_ruby_rack_result_status(VALUE result);
-static int nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status);
+static int nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env);
+nxt_inline void nxt_ruby_add_sptr(VALUE hash_env, VALUE name,
+ nxt_unit_sptr_t *sptr, uint32_t len);
+static nxt_int_t nxt_ruby_rack_result_status(nxt_unit_request_info_t *req,
+ VALUE result);
+static int nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req,
+ VALUE result, nxt_int_t status);
static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg);
static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg);
-static int nxt_ruby_rack_result_body(VALUE result);
-static int nxt_ruby_rack_result_body_file_write(VALUE filepath);
+static int nxt_ruby_rack_result_body(nxt_unit_request_info_t *req,
+ VALUE result);
+static int nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req,
+ VALUE filepath);
+static void *nxt_ruby_response_write_cb(void *read_info);
static VALUE nxt_ruby_rack_result_body_each(VALUE body, VALUE arg,
int argc, const VALUE *argv, VALUE blockarg);
+static void *nxt_ruby_response_write(void *body);
-static void nxt_ruby_exception_log(nxt_task_t *task, uint32_t level,
- const char *desc);
+static void nxt_ruby_exception_log(nxt_unit_request_info_t *req,
+ uint32_t level, const char *desc);
+static void nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx);
static void nxt_ruby_atexit(void);
@@ -68,12 +75,11 @@ static uint32_t compat[] = {
NXT_VERNUM, NXT_DEBUG,
};
-static VALUE nxt_ruby_rackup;
-static VALUE nxt_ruby_call;
-static VALUE nxt_ruby_env;
-static VALUE nxt_ruby_io_input;
-static VALUE nxt_ruby_io_error;
-static nxt_ruby_run_ctx_t nxt_ruby_run_ctx;
+static VALUE nxt_ruby_rackup;
+static VALUE nxt_ruby_call;
+
+static uint32_t nxt_ruby_threads;
+static nxt_ruby_ctx_t *nxt_ruby_ctxs;
NXT_EXPORT nxt_app_module_t nxt_app_module = {
sizeof(compat),
@@ -86,83 +92,207 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = {
nxt_ruby_start,
};
+typedef struct {
+ nxt_str_t string;
+ VALUE *v;
+} nxt_ruby_string_t;
+
+static VALUE nxt_rb_80_str;
+static VALUE nxt_rb_content_length_str;
+static VALUE nxt_rb_content_type_str;
+static VALUE nxt_rb_http_str;
+static VALUE nxt_rb_https_str;
+static VALUE nxt_rb_path_info_str;
+static VALUE nxt_rb_query_string_str;
+static VALUE nxt_rb_rack_url_scheme_str;
+static VALUE nxt_rb_remote_addr_str;
+static VALUE nxt_rb_request_method_str;
+static VALUE nxt_rb_request_uri_str;
+static VALUE nxt_rb_server_addr_str;
+static VALUE nxt_rb_server_name_str;
+static VALUE nxt_rb_server_port_str;
+static VALUE nxt_rb_server_protocol_str;
+
+static nxt_ruby_string_t nxt_rb_strings[] = {
+ { nxt_string("80"), &nxt_rb_80_str },
+ { nxt_string("CONTENT_LENGTH"), &nxt_rb_content_length_str },
+ { nxt_string("CONTENT_TYPE"), &nxt_rb_content_type_str },
+ { nxt_string("http"), &nxt_rb_http_str },
+ { nxt_string("https"), &nxt_rb_https_str },
+ { nxt_string("PATH_INFO"), &nxt_rb_path_info_str },
+ { nxt_string("QUERY_STRING"), &nxt_rb_query_string_str },
+ { nxt_string("rack.url_scheme"), &nxt_rb_rack_url_scheme_str },
+ { nxt_string("REMOTE_ADDR"), &nxt_rb_remote_addr_str },
+ { nxt_string("REQUEST_METHOD"), &nxt_rb_request_method_str },
+ { nxt_string("REQUEST_URI"), &nxt_rb_request_uri_str },
+ { nxt_string("SERVER_ADDR"), &nxt_rb_server_addr_str },
+ { nxt_string("SERVER_NAME"), &nxt_rb_server_name_str },
+ { nxt_string("SERVER_PORT"), &nxt_rb_server_port_str },
+ { nxt_string("SERVER_PROTOCOL"), &nxt_rb_server_protocol_str },
+ { nxt_null_string, NULL },
+};
+
+
+static int
+nxt_ruby_init_strings(void)
+{
+ VALUE v;
+ nxt_ruby_string_t *pstr;
+
+ pstr = nxt_rb_strings;
+
+ while (pstr->string.start != NULL) {
+ v = rb_str_new_static((char *) pstr->string.start, pstr->string.length);
+
+ if (nxt_slow_path(v == Qnil)) {
+ nxt_unit_alert(NULL, "Ruby: failed to create const string '%.*s'",
+ (int) pstr->string.length,
+ (char *) pstr->string.start);
+
+ return NXT_UNIT_ERROR;
+ }
+
+ *pstr->v = v;
+
+ rb_gc_register_address(pstr->v);
+
+ pstr++;
+ }
+
+ return NXT_UNIT_OK;
+}
+
+
+static void
+nxt_ruby_done_strings(void)
+{
+ nxt_ruby_string_t *pstr;
+
+ pstr = nxt_rb_strings;
+
+ while (pstr->string.start != NULL) {
+ rb_gc_unregister_address(pstr->v);
+
+ *pstr->v = Qnil;
+
+ pstr++;
+ }
+}
+
static nxt_int_t
nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data)
{
int state, rc;
VALUE res;
+ nxt_ruby_ctx_t ruby_ctx;
nxt_unit_ctx_t *unit_ctx;
nxt_unit_init_t ruby_unit_init;
+ nxt_ruby_app_conf_t *c;
nxt_ruby_rack_init_t rack_init;
nxt_common_app_conf_t *conf;
static char *argv[2] = { (char *) "NGINX_Unit", (char *) "-e0" };
conf = data->app;
+ c = &conf->u.ruby;
+
+ nxt_ruby_threads = c->threads;
RUBY_INIT_STACK
ruby_init();
ruby_options(2, argv);
ruby_script("NGINX_Unit");
+ ruby_ctx.env = Qnil;
+ ruby_ctx.io_input = Qnil;
+ ruby_ctx.io_error = Qnil;
+ ruby_ctx.thread = Qnil;
+ ruby_ctx.ctx = NULL;
+ ruby_ctx.req = NULL;
+
rack_init.task = task;
- rack_init.script = &conf->u.ruby.script;
+ rack_init.script = &c->script;
+ rack_init.rctx = &ruby_ctx;
+
+ nxt_ruby_init_strings();
res = rb_protect(nxt_ruby_init_basic,
(VALUE) (uintptr_t) &rack_init, &state);
if (nxt_slow_path(res == Qnil || state != 0)) {
- nxt_ruby_exception_log(task, NXT_LOG_ALERT,
+ nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
"Failed to init basic variables");
return NXT_ERROR;
}
+ nxt_ruby_call = Qnil;
+
nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init);
if (nxt_slow_path(nxt_ruby_rackup == Qnil)) {
return NXT_ERROR;
}
+ rb_gc_register_address(&nxt_ruby_rackup);
+
nxt_ruby_call = rb_intern("call");
if (nxt_slow_path(nxt_ruby_call == Qnil)) {
nxt_alert(task, "Ruby: Unable to find rack entry point");
- return NXT_ERROR;
+ goto fail;
}
- nxt_ruby_env = rb_protect(nxt_ruby_rack_env_create, Qnil, &state);
- if (nxt_slow_path(state != 0)) {
- nxt_ruby_exception_log(task, NXT_LOG_ALERT,
+ rb_gc_register_address(&nxt_ruby_call);
+
+ ruby_ctx.env = rb_protect(nxt_ruby_rack_env_create,
+ (VALUE) (uintptr_t) &ruby_ctx, &state);
+ if (nxt_slow_path(ruby_ctx.env == Qnil || state != 0)) {
+ nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
"Failed to create 'environ' variable");
- return NXT_ERROR;
+ goto fail;
}
- rb_gc_register_address(&nxt_ruby_rackup);
- rb_gc_register_address(&nxt_ruby_call);
- rb_gc_register_address(&nxt_ruby_env);
+ rc = nxt_ruby_init_threads(c);
+ if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
+ goto fail;
+ }
nxt_unit_default_init(task, &ruby_unit_init);
ruby_unit_init.callbacks.request_handler = nxt_ruby_request_handler;
+ ruby_unit_init.callbacks.ready_handler = nxt_ruby_ready_handler;
ruby_unit_init.shm_limit = conf->shm_limit;
+ ruby_unit_init.data = c;
+ ruby_unit_init.ctx_data = &ruby_ctx;
unit_ctx = nxt_unit_init(&ruby_unit_init);
if (nxt_slow_path(unit_ctx == NULL)) {
- return NXT_ERROR;
+ goto fail;
}
- nxt_ruby_run_ctx.unit_ctx = unit_ctx;
+ rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_unit_run, unit_ctx,
+ nxt_ruby_ubf, unit_ctx);
- rc = nxt_unit_run(unit_ctx);
+ nxt_ruby_join_threads(unit_ctx, c);
- nxt_ruby_atexit();
+ nxt_unit_done(unit_ctx);
- nxt_ruby_run_ctx.unit_ctx = NULL;
+ nxt_ruby_ctx_done(&ruby_ctx);
- nxt_unit_done(unit_ctx);
+ nxt_ruby_atexit();
exit(rc);
return NXT_OK;
+
+fail:
+
+ nxt_ruby_join_threads(NULL, c);
+
+ nxt_ruby_ctx_done(&ruby_ctx);
+
+ nxt_ruby_atexit();
+
+ return NXT_ERROR;
}
@@ -170,7 +300,6 @@ static VALUE
nxt_ruby_init_basic(VALUE arg)
{
int state;
- nxt_int_t rc;
nxt_ruby_rack_init_t *rack_init;
rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) arg;
@@ -186,56 +315,19 @@ nxt_ruby_init_basic(VALUE arg)
rb_funcall(rb_cObject, rb_intern("require"), 1,
rb_str_new2("enc/trans/transdb"));
- rc = nxt_ruby_init_io(rack_init->task);
- if (nxt_slow_path(rc != NXT_OK)) {
- return Qnil;
- }
-
return arg;
}
-static nxt_int_t
-nxt_ruby_init_io(nxt_task_t *task)
-{
- VALUE rb, io_input, io_error;
-
- io_input = nxt_ruby_stream_io_input_init();
- rb = Data_Wrap_Struct(io_input, 0, 0, &nxt_ruby_run_ctx);
-
- nxt_ruby_io_input = rb_funcall(io_input, rb_intern("new"), 1, rb);
- if (nxt_slow_path(nxt_ruby_io_input == Qnil)) {
- nxt_alert(task, "Ruby: Failed to create environment 'rack.input' var");
-
- return NXT_ERROR;
- }
-
- io_error = nxt_ruby_stream_io_error_init();
- rb = Data_Wrap_Struct(io_error, 0, 0, &nxt_ruby_run_ctx);
-
- nxt_ruby_io_error = rb_funcall(io_error, rb_intern("new"), 1, rb);
- if (nxt_slow_path(nxt_ruby_io_error == Qnil)) {
- nxt_alert(task, "Ruby: Failed to create environment 'rack.error' var");
-
- return NXT_ERROR;
- }
-
- rb_gc_register_address(&nxt_ruby_io_input);
- rb_gc_register_address(&nxt_ruby_io_error);
-
- return NXT_OK;
-}
-
-
static VALUE
nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init)
{
int state;
- VALUE rack, rackup, err;
+ VALUE rackup, err;
rb_protect(nxt_ruby_require_rubygems, Qnil, &state);
if (nxt_slow_path(state != 0)) {
- nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT,
+ nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
"Failed to require 'rubygems' package");
return Qnil;
}
@@ -245,7 +337,7 @@ nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init)
err = rb_errinfo();
if (rb_obj_is_kind_of(err, rb_eLoadError) == Qfalse) {
- nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT,
+ nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
"Failed to require 'bundler/setup' package");
return Qnil;
}
@@ -255,18 +347,15 @@ nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init)
rb_protect(nxt_ruby_require_rack, Qnil, &state);
if (nxt_slow_path(state != 0)) {
- nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT,
+ nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
"Failed to require 'rack' package");
return Qnil;
}
- rack = rb_const_get(rb_cObject, rb_intern("Rack"));
- rack_init->builder = rb_const_get(rack, rb_intern("Builder"));
-
rackup = rb_protect(nxt_ruby_rack_parse_script,
(VALUE) (uintptr_t) rack_init, &state);
if (nxt_slow_path(TYPE(rackup) != T_ARRAY || state != 0)) {
- nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT,
+ nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
"Failed to parse rack script");
return Qnil;
}
@@ -306,15 +395,18 @@ nxt_ruby_require_rack(VALUE arg)
static VALUE
nxt_ruby_rack_parse_script(VALUE ctx)
{
- VALUE script, res;
+ VALUE script, res, rack, builder;
nxt_ruby_rack_init_t *rack_init;
rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) ctx;
+ rack = rb_const_get(rb_cObject, rb_intern("Rack"));
+ builder = rb_const_get(rack, rb_intern("Builder"));
+
script = rb_str_new((const char *) rack_init->script->start,
(long) rack_init->script->length);
- res = rb_funcall(rack_init->builder, rb_intern("parse_file"), 1, script);
+ res = rb_funcall(builder, rb_intern("parse_file"), 1, script);
rb_str_free(script);
@@ -325,7 +417,16 @@ nxt_ruby_rack_parse_script(VALUE ctx)
static VALUE
nxt_ruby_rack_env_create(VALUE arg)
{
- VALUE hash_env, version;
+ int rc;
+ VALUE hash_env, version;
+ nxt_ruby_ctx_t *rctx;
+
+ rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg;
+
+ rc = nxt_ruby_init_io(rctx);
+ if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ return Qnil;
+ }
hash_env = rb_hash_new();
@@ -339,47 +440,114 @@ nxt_ruby_rack_env_create(VALUE arg)
rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR));
rb_hash_aset(hash_env, rb_str_new2("rack.version"), version);
- rb_hash_aset(hash_env, rb_str_new2("rack.input"), nxt_ruby_io_input);
- rb_hash_aset(hash_env, rb_str_new2("rack.errors"), nxt_ruby_io_error);
- rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), Qfalse);
+ rb_hash_aset(hash_env, rb_str_new2("rack.input"), rctx->io_input);
+ rb_hash_aset(hash_env, rb_str_new2("rack.errors"), rctx->io_error);
+ rb_hash_aset(hash_env, rb_str_new2("rack.multithread"),
+ nxt_ruby_threads > 1 ? Qtrue : Qfalse);
rb_hash_aset(hash_env, rb_str_new2("rack.multiprocess"), Qtrue);
rb_hash_aset(hash_env, rb_str_new2("rack.run_once"), Qfalse);
rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse);
rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil);
rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil);
+ rctx->env = hash_env;
+
+ rb_gc_register_address(&rctx->env);
+
return hash_env;
}
+static int
+nxt_ruby_init_io(nxt_ruby_ctx_t *rctx)
+{
+ VALUE io_input, io_error;
+
+ io_input = nxt_ruby_stream_io_input_init();
+
+ rctx->io_input = rb_funcall(io_input, rb_intern("new"), 1,
+ (VALUE) (uintptr_t) rctx);
+ if (nxt_slow_path(rctx->io_input == Qnil)) {
+ nxt_unit_alert(NULL,
+ "Ruby: Failed to create environment 'rack.input' var");
+
+ return NXT_UNIT_ERROR;
+ }
+
+ rb_gc_register_address(&rctx->io_input);
+
+ io_error = nxt_ruby_stream_io_error_init();
+
+ rctx->io_error = rb_funcall(io_error, rb_intern("new"), 1,
+ (VALUE) (uintptr_t) rctx);
+ if (nxt_slow_path(rctx->io_error == Qnil)) {
+ nxt_unit_alert(NULL,
+ "Ruby: Failed to create environment 'rack.error' var");
+
+ return NXT_UNIT_ERROR;
+ }
+
+ rb_gc_register_address(&rctx->io_error);
+
+ return NXT_UNIT_OK;
+}
+
+
static void
nxt_ruby_request_handler(nxt_unit_request_info_t *req)
{
- int state;
- VALUE res;
+ (void) rb_thread_call_with_gvl(nxt_ruby_request_handler_gvl, req);
+}
- nxt_ruby_run_ctx.req = req;
- res = rb_protect(nxt_ruby_rack_app_run, Qnil, &state);
+static void *
+nxt_ruby_request_handler_gvl(void *data)
+{
+ int state;
+ VALUE res;
+ nxt_ruby_ctx_t *rctx;
+ nxt_unit_request_info_t *req;
+
+ req = data;
+
+ rctx = req->ctx->data;
+ rctx->req = req;
+
+ res = rb_protect(nxt_ruby_rack_app_run, (VALUE) (uintptr_t) req, &state);
if (nxt_slow_path(res == Qnil || state != 0)) {
- nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
+ nxt_ruby_exception_log(req, NXT_LOG_ERR,
"Failed to run ruby script");
+
+ nxt_unit_request_done(req, NXT_UNIT_ERROR);
+
+ } else {
+ nxt_unit_request_done(req, NXT_UNIT_OK);
}
+
+ rctx->req = NULL;
+
+ return NULL;
}
static VALUE
nxt_ruby_rack_app_run(VALUE arg)
{
- int rc;
- VALUE env, result;
- nxt_int_t status;
+ int rc;
+ VALUE env, result;
+ nxt_int_t status;
+ nxt_ruby_ctx_t *rctx;
+ nxt_unit_request_info_t *req;
- env = rb_hash_dup(nxt_ruby_env);
+ req = (nxt_unit_request_info_t *) arg;
- rc = nxt_ruby_read_request(env);
+ rctx = req->ctx->data;
+
+ env = rb_hash_dup(rctx->env);
+
+ rc = nxt_ruby_read_request(req, env);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- nxt_unit_req_alert(nxt_ruby_run_ctx.req,
+ nxt_unit_req_alert(req,
"Ruby: Failed to process incoming request");
goto fail;
@@ -387,50 +555,44 @@ nxt_ruby_rack_app_run(VALUE arg)
result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env);
if (nxt_slow_path(TYPE(result) != T_ARRAY)) {
- nxt_unit_req_error(nxt_ruby_run_ctx.req,
+ nxt_unit_req_error(req,
"Ruby: Invalid response format from application");
goto fail;
}
if (nxt_slow_path(RARRAY_LEN(result) != 3)) {
- nxt_unit_req_error(nxt_ruby_run_ctx.req,
+ nxt_unit_req_error(req,
"Ruby: Invalid response format from application. "
"Need 3 entries [Status, Headers, Body]");
goto fail;
}
- status = nxt_ruby_rack_result_status(result);
+ status = nxt_ruby_rack_result_status(req, result);
if (nxt_slow_path(status < 0)) {
- nxt_unit_req_error(nxt_ruby_run_ctx.req,
+ nxt_unit_req_error(req,
"Ruby: Invalid response status from application.");
goto fail;
}
- rc = nxt_ruby_rack_result_headers(result, status);
+ rc = nxt_ruby_rack_result_headers(req, result, status);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
goto fail;
}
- rc = nxt_ruby_rack_result_body(result);
+ rc = nxt_ruby_rack_result_body(req, result);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
goto fail;
}
- nxt_unit_request_done(nxt_ruby_run_ctx.req, rc);
- nxt_ruby_run_ctx.req = NULL;
-
rb_hash_delete(env, rb_obj_id(env));
return result;
fail:
- nxt_unit_request_done(nxt_ruby_run_ctx.req, NXT_UNIT_ERROR);
- nxt_ruby_run_ctx.req = NULL;
-
rb_hash_delete(env, rb_obj_id(env));
return Qnil;
@@ -438,85 +600,76 @@ fail:
static int
-nxt_ruby_read_request(VALUE hash_env)
+nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env)
{
+ VALUE name;
uint32_t i;
nxt_unit_field_t *f;
nxt_unit_request_t *r;
- r = nxt_ruby_run_ctx.req->request;
+ r = req->request;
-#define NL(S) (S), sizeof(S)-1
-
- nxt_ruby_add_sptr(hash_env, NL("REQUEST_METHOD"), &r->method,
+ nxt_ruby_add_sptr(hash_env, nxt_rb_request_method_str, &r->method,
r->method_length);
- nxt_ruby_add_sptr(hash_env, NL("REQUEST_URI"), &r->target,
+ nxt_ruby_add_sptr(hash_env, nxt_rb_request_uri_str, &r->target,
r->target_length);
- nxt_ruby_add_sptr(hash_env, NL("PATH_INFO"), &r->path, r->path_length);
- nxt_ruby_add_sptr(hash_env, NL("QUERY_STRING"), &r->query,
+ nxt_ruby_add_sptr(hash_env, nxt_rb_path_info_str, &r->path, r->path_length);
+ nxt_ruby_add_sptr(hash_env, nxt_rb_query_string_str, &r->query,
r->query_length);
- nxt_ruby_add_sptr(hash_env, NL("SERVER_PROTOCOL"), &r->version,
+ nxt_ruby_add_sptr(hash_env, nxt_rb_server_protocol_str, &r->version,
r->version_length);
- nxt_ruby_add_sptr(hash_env, NL("REMOTE_ADDR"), &r->remote,
+ nxt_ruby_add_sptr(hash_env, nxt_rb_remote_addr_str, &r->remote,
r->remote_length);
- nxt_ruby_add_sptr(hash_env, NL("SERVER_ADDR"), &r->local, r->local_length);
-
- nxt_ruby_add_sptr(hash_env, NL("SERVER_NAME"), &r->server_name,
+ nxt_ruby_add_sptr(hash_env, nxt_rb_server_addr_str, &r->local,
+ r->local_length);
+ nxt_ruby_add_sptr(hash_env, nxt_rb_server_name_str, &r->server_name,
r->server_name_length);
- nxt_ruby_add_str(hash_env, NL("SERVER_PORT"), "80", 2);
- rb_hash_aset(hash_env, rb_str_new2("rack.url_scheme"),
- r->tls ? rb_str_new2("https") : rb_str_new2("http"));
+ rb_hash_aset(hash_env, nxt_rb_server_port_str, nxt_rb_80_str);
+
+ rb_hash_aset(hash_env, nxt_rb_rack_url_scheme_str,
+ r->tls ? nxt_rb_https_str : nxt_rb_http_str);
for (i = 0; i < r->fields_count; i++) {
f = r->fields + i;
- nxt_ruby_add_sptr(hash_env, nxt_unit_sptr_get(&f->name), f->name_length,
- &f->value, f->value_length);
+ name = rb_str_new(nxt_unit_sptr_get(&f->name), f->name_length);
+
+ nxt_ruby_add_sptr(hash_env, name, &f->value, f->value_length);
}
if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
f = r->fields + r->content_length_field;
- nxt_ruby_add_sptr(hash_env, NL("CONTENT_LENGTH"),
+ nxt_ruby_add_sptr(hash_env, nxt_rb_content_length_str,
&f->value, f->value_length);
}
if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
f = r->fields + r->content_type_field;
- nxt_ruby_add_sptr(hash_env, NL("CONTENT_TYPE"),
+ nxt_ruby_add_sptr(hash_env, nxt_rb_content_type_str,
&f->value, f->value_length);
}
-#undef NL
-
return NXT_UNIT_OK;
}
nxt_inline void
-nxt_ruby_add_sptr(VALUE hash_env,
- const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len)
+nxt_ruby_add_sptr(VALUE hash_env, VALUE name,
+ nxt_unit_sptr_t *sptr, uint32_t len)
{
char *str;
str = nxt_unit_sptr_get(sptr);
- rb_hash_aset(hash_env, rb_str_new(name, name_len), rb_str_new(str, len));
-}
-
-
-nxt_inline void
-nxt_ruby_add_str(VALUE hash_env,
- const char *name, uint32_t name_len, const char *str, uint32_t len)
-{
- rb_hash_aset(hash_env, rb_str_new(name, name_len), rb_str_new(str, len));
+ rb_hash_aset(hash_env, name, rb_str_new(str, len));
}
static nxt_int_t
-nxt_ruby_rack_result_status(VALUE result)
+nxt_ruby_rack_result_status(nxt_unit_request_info_t *req, VALUE result)
{
VALUE status;
@@ -531,7 +684,7 @@ nxt_ruby_rack_result_status(VALUE result)
RSTRING_LEN(status));
}
- nxt_unit_req_error(nxt_ruby_run_ctx.req, "Ruby: Invalid response 'status' "
+ nxt_unit_req_error(req, "Ruby: Invalid response 'status' "
"format from application");
return -2;
@@ -539,14 +692,16 @@ nxt_ruby_rack_result_status(VALUE result)
typedef struct {
- int rc;
- uint32_t fields;
- uint32_t size;
+ int rc;
+ uint32_t fields;
+ uint32_t size;
+ nxt_unit_request_info_t *req;
} nxt_ruby_headers_info_t;
static int
-nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status)
+nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, VALUE result,
+ nxt_int_t status)
{
int rc;
VALUE headers;
@@ -554,7 +709,7 @@ nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status)
headers = rb_ary_entry(result, 1);
if (nxt_slow_path(TYPE(headers) != T_HASH)) {
- nxt_unit_req_error(nxt_ruby_run_ctx.req,
+ nxt_unit_req_error(req,
"Ruby: Invalid response 'headers' format from "
"application");
@@ -566,6 +721,7 @@ nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status)
headers_info.rc = NXT_UNIT_OK;
headers_info.fields = 0;
headers_info.size = 0;
+ headers_info.req = req;
rb_hash_foreach(headers, nxt_ruby_hash_info,
(VALUE) (uintptr_t) &headers_info);
@@ -573,13 +729,14 @@ nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status)
return headers_info.rc;
}
- rc = nxt_unit_response_init(nxt_ruby_run_ctx.req, status,
+ rc = nxt_unit_response_init(req, status,
headers_info.fields, headers_info.size);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
return rc;
}
- rb_hash_foreach(headers, nxt_ruby_hash_add, (VALUE) (uintptr_t) &rc);
+ rb_hash_foreach(headers, nxt_ruby_hash_add,
+ (VALUE) (uintptr_t) &headers_info);
return rc;
}
@@ -594,14 +751,14 @@ nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg)
headers_info = (void *) (uintptr_t) arg;
if (nxt_slow_path(TYPE(r_key) != T_STRING)) {
- nxt_unit_req_error(nxt_ruby_run_ctx.req,
+ nxt_unit_req_error(headers_info->req,
"Ruby: Wrong header entry 'key' from application");
goto fail;
}
if (nxt_slow_path(TYPE(r_value) != T_STRING)) {
- nxt_unit_req_error(nxt_ruby_run_ctx.req,
+ nxt_unit_req_error(headers_info->req,
"Ruby: Wrong header entry 'value' from application");
goto fail;
@@ -644,11 +801,13 @@ fail:
static int
nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg)
{
- int *rc;
- uint32_t key_len;
- const char *value, *value_end, *pos;
+ int *rc;
+ uint32_t key_len;
+ const char *value, *value_end, *pos;
+ nxt_ruby_headers_info_t *headers_info;
- rc = (int *) (uintptr_t) arg;
+ headers_info = (void *) (uintptr_t) arg;
+ rc = &headers_info->rc;
value = RSTRING_PTR(r_value);
value_end = value + RSTRING_LEN(r_value);
@@ -664,7 +823,7 @@ nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg)
break;
}
- *rc = nxt_unit_response_add_field(nxt_ruby_run_ctx.req,
+ *rc = nxt_unit_response_add_field(headers_info->req,
RSTRING_PTR(r_key), key_len,
value, pos - value);
if (nxt_slow_path(*rc != NXT_UNIT_OK)) {
@@ -676,7 +835,7 @@ nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg)
}
if (value <= value_end) {
- *rc = nxt_unit_response_add_field(nxt_ruby_run_ctx.req,
+ *rc = nxt_unit_response_add_field(headers_info->req,
RSTRING_PTR(r_key), key_len,
value, value_end - value);
if (nxt_slow_path(*rc != NXT_UNIT_OK)) {
@@ -695,7 +854,7 @@ fail:
static int
-nxt_ruby_rack_result_body(VALUE result)
+nxt_ruby_rack_result_body(nxt_unit_request_info_t *req, VALUE result)
{
int rc;
VALUE fn, body;
@@ -706,24 +865,24 @@ nxt_ruby_rack_result_body(VALUE result)
fn = rb_funcall(body, rb_intern("to_path"), 0);
if (nxt_slow_path(TYPE(fn) != T_STRING)) {
- nxt_unit_req_error(nxt_ruby_run_ctx.req,
+ nxt_unit_req_error(req,
"Ruby: Failed to get 'body' file path from "
"application");
return NXT_UNIT_ERROR;
}
- rc = nxt_ruby_rack_result_body_file_write(fn);
+ rc = nxt_ruby_rack_result_body_file_write(req, fn);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
return rc;
}
} else if (rb_respond_to(body, rb_intern("each"))) {
rb_block_call(body, rb_intern("each"), 0, 0,
- nxt_ruby_rack_result_body_each, 0);
+ nxt_ruby_rack_result_body_each, (VALUE) (uintptr_t) req);
} else {
- nxt_unit_req_error(nxt_ruby_run_ctx.req,
+ nxt_unit_req_error(req,
"Ruby: Invalid response 'body' format "
"from application");
@@ -772,17 +931,24 @@ nxt_ruby_rack_file_read(nxt_unit_read_info_t *read_info, void *dst, size_t size)
}
+typedef struct {
+ nxt_unit_read_info_t read_info;
+ nxt_unit_request_info_t *req;
+} nxt_ruby_read_info_t;
+
+
static int
-nxt_ruby_rack_result_body_file_write(VALUE filepath)
+nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req,
+ VALUE filepath)
{
int fd, rc;
struct stat finfo;
nxt_ruby_rack_file_t ruby_file;
- nxt_unit_read_info_t read_info;
+ nxt_ruby_read_info_t ri;
fd = open(RSTRING_PTR(filepath), O_RDONLY, 0);
if (nxt_slow_path(fd == -1)) {
- nxt_unit_req_error(nxt_ruby_run_ctx.req,
+ nxt_unit_req_error(req,
"Ruby: Failed to open content file \"%s\": %s (%d)",
RSTRING_PTR(filepath), strerror(errno), errno);
@@ -791,7 +957,7 @@ nxt_ruby_rack_result_body_file_write(VALUE filepath)
rc = fstat(fd, &finfo);
if (nxt_slow_path(rc == -1)) {
- nxt_unit_req_error(nxt_ruby_run_ctx.req,
+ nxt_unit_req_error(req,
"Ruby: Content file fstat(\"%s\") failed: %s (%d)",
RSTRING_PTR(filepath), strerror(errno), errno);
@@ -804,16 +970,16 @@ nxt_ruby_rack_result_body_file_write(VALUE filepath)
ruby_file.pos = 0;
ruby_file.rest = finfo.st_size;
- read_info.read = nxt_ruby_rack_file_read;
- read_info.eof = ruby_file.rest == 0;
- read_info.buf_size = ruby_file.rest;
- read_info.data = &ruby_file;
+ ri.read_info.read = nxt_ruby_rack_file_read;
+ ri.read_info.eof = ruby_file.rest == 0;
+ ri.read_info.buf_size = ruby_file.rest;
+ ri.read_info.data = &ruby_file;
+ ri.req = req;
- rc = nxt_unit_response_write_cb(nxt_ruby_run_ctx.req, &read_info);
- if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- nxt_unit_req_error(nxt_ruby_run_ctx.req,
- "Ruby: Failed to write content file.");
- }
+ rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_response_write_cb,
+ &ri,
+ nxt_ruby_ubf,
+ req->ctx);
close(fd);
@@ -821,39 +987,77 @@ nxt_ruby_rack_result_body_file_write(VALUE filepath)
}
+static void *
+nxt_ruby_response_write_cb(void *data)
+{
+ int rc;
+ nxt_ruby_read_info_t *ri;
+
+ ri = data;
+
+ rc = nxt_unit_response_write_cb(ri->req, &ri->read_info);
+ if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ nxt_unit_req_error(ri->req, "Ruby: Failed to write content file.");
+ }
+
+ return (void *) (intptr_t) rc;
+}
+
+
+typedef struct {
+ VALUE body;
+ nxt_unit_request_info_t *req;
+} nxt_ruby_write_info_t;
+
+
static VALUE
nxt_ruby_rack_result_body_each(VALUE body, VALUE arg, int argc,
const VALUE *argv, VALUE blockarg)
{
- int rc;
+ nxt_ruby_write_info_t wi;
if (TYPE(body) != T_STRING) {
return Qnil;
}
- rc = nxt_unit_response_write(nxt_ruby_run_ctx.req, RSTRING_PTR(body),
- RSTRING_LEN(body));
+ wi.body = body;
+ wi.req = (void *) (uintptr_t) arg;
+
+ (void) rb_thread_call_without_gvl(nxt_ruby_response_write,
+ (void *) (uintptr_t) &wi,
+ nxt_ruby_ubf, wi.req->ctx);
+
+ return Qnil;
+}
+
+
+static void *
+nxt_ruby_response_write(void *data)
+{
+ int rc;
+ nxt_ruby_write_info_t *wi;
+
+ wi = data;
+
+ rc = nxt_unit_response_write(wi->req, RSTRING_PTR(wi->body),
+ RSTRING_LEN(wi->body));
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- nxt_unit_req_error(nxt_ruby_run_ctx.req,
+ nxt_unit_req_error(wi->req,
"Ruby: Failed to write 'body' from application");
}
- return Qnil;
+ return (void *) (intptr_t) rc;
}
static void
-nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc)
+nxt_ruby_exception_log(nxt_unit_request_info_t *req, uint32_t level,
+ const char *desc)
{
int i;
VALUE err, ary, eclass, msg;
- if (task != NULL) {
- nxt_log(task, level, "Ruby: %s", desc);
-
- } else {
- nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "Ruby: %s", desc);
- }
+ nxt_unit_req_log(req, level, "Ruby: %s", desc);
err = rb_errinfo();
if (nxt_slow_path(err == Qnil)) {
@@ -868,25 +1072,30 @@ nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc)
eclass = rb_class_name(rb_class_of(err));
msg = rb_funcall(err, rb_intern("message"), 0);
- if (task != NULL) {
- nxt_log(task, level, "Ruby: %s: %s (%s)",
- RSTRING_PTR(RARRAY_PTR(ary)[0]),
- RSTRING_PTR(msg), RSTRING_PTR(eclass));
-
- } else {
- nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "Ruby: %s: %s (%s)",
+ nxt_unit_req_log(req, level, "Ruby: %s: %s (%s)",
RSTRING_PTR(RARRAY_PTR(ary)[0]),
RSTRING_PTR(msg), RSTRING_PTR(eclass));
- }
for (i = 1; i < RARRAY_LEN(ary); i++) {
- if (task != NULL) {
- nxt_log(task, level, "from %s", RSTRING_PTR(RARRAY_PTR(ary)[i]));
-
- } else {
- nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "from %s",
+ nxt_unit_req_log(req, level, "from %s",
RSTRING_PTR(RARRAY_PTR(ary)[i]));
- }
+ }
+}
+
+
+static void
+nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx)
+{
+ if (rctx->io_input != Qnil) {
+ rb_gc_unregister_address(&rctx->io_input);
+ }
+
+ if (rctx->io_error != Qnil) {
+ rb_gc_unregister_address(&rctx->io_error);
+ }
+
+ if (rctx->env != Qnil) {
+ rb_gc_unregister_address(&rctx->env);
}
}
@@ -894,12 +1103,174 @@ nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc)
static void
nxt_ruby_atexit(void)
{
- rb_gc_unregister_address(&nxt_ruby_io_input);
- rb_gc_unregister_address(&nxt_ruby_io_error);
+ if (nxt_ruby_rackup != Qnil) {
+ rb_gc_unregister_address(&nxt_ruby_rackup);
+ }
- rb_gc_unregister_address(&nxt_ruby_rackup);
- rb_gc_unregister_address(&nxt_ruby_call);
- rb_gc_unregister_address(&nxt_ruby_env);
+ if (nxt_ruby_call != Qnil) {
+ rb_gc_unregister_address(&nxt_ruby_call);
+ }
+
+ nxt_ruby_done_strings();
ruby_cleanup(0);
}
+
+
+static int
+nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx)
+{
+ VALUE res;
+ uint32_t i;
+ nxt_ruby_ctx_t *rctx;
+ nxt_ruby_app_conf_t *c;
+
+ /* Worker thread context. */
+ if (!nxt_unit_is_main_ctx(ctx)) {
+ return NXT_UNIT_OK;
+ }
+
+ c = ctx->unit->data;
+
+ if (c->threads <= 1) {
+ return NXT_UNIT_OK;
+ }
+
+ for (i = 0; i < c->threads - 1; i++) {
+ rctx = &nxt_ruby_ctxs[i];
+
+ rctx->ctx = ctx;
+
+ res = rb_thread_create(RUBY_METHOD_FUNC(nxt_ruby_thread_func), rctx);
+
+ if (nxt_fast_path(res != Qnil)) {
+ nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1));
+
+ rctx->thread = res;
+
+ } else {
+ nxt_unit_alert(ctx, "thread #%d create failed", (int) (i + 1));
+
+ return NXT_UNIT_ERROR;
+ }
+ }
+
+ return NXT_UNIT_OK;
+}
+
+
+static VALUE
+nxt_ruby_thread_func(VALUE arg)
+{
+ nxt_unit_ctx_t *ctx;
+ nxt_ruby_ctx_t *rctx;
+
+ rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg;
+
+ nxt_unit_debug(rctx->ctx, "worker thread start");
+
+ ctx = nxt_unit_ctx_alloc(rctx->ctx, rctx);
+ if (nxt_slow_path(ctx == NULL)) {
+ goto fail;
+ }
+
+ (void) rb_thread_call_without_gvl(nxt_ruby_unit_run, ctx,
+ nxt_ruby_ubf, ctx);
+
+ nxt_unit_done(ctx);
+
+fail:
+
+ nxt_unit_debug(NULL, "worker thread end");
+
+ return Qnil;
+}
+
+
+static void *
+nxt_ruby_unit_run(void *ctx)
+{
+ return (void *) (intptr_t) nxt_unit_run(ctx);
+}
+
+
+static void
+nxt_ruby_ubf(void *ctx)
+{
+ nxt_unit_warn(ctx, "Ruby: UBF");
+}
+
+
+static int
+nxt_ruby_init_threads(nxt_ruby_app_conf_t *c)
+{
+ int state;
+ uint32_t i;
+ nxt_ruby_ctx_t *rctx;
+
+ if (c->threads <= 1) {
+ return NXT_UNIT_OK;
+ }
+
+ nxt_ruby_ctxs = nxt_unit_malloc(NULL, sizeof(nxt_ruby_ctx_t)
+ * (c->threads - 1));
+ if (nxt_slow_path(nxt_ruby_ctxs == NULL)) {
+ nxt_unit_alert(NULL, "Failed to allocate run contexts array");
+
+ return NXT_UNIT_ERROR;
+ }
+
+ for (i = 0; i < c->threads - 1; i++) {
+ rctx = &nxt_ruby_ctxs[i];
+
+ rctx->env = Qnil;
+ rctx->io_input = Qnil;
+ rctx->io_error = Qnil;
+ rctx->thread = Qnil;
+ }
+
+ for (i = 0; i < c->threads - 1; i++) {
+ rctx = &nxt_ruby_ctxs[i];
+
+ rctx->env = rb_protect(nxt_ruby_rack_env_create,
+ (VALUE) (uintptr_t) rctx, &state);
+ if (nxt_slow_path(rctx->env == Qnil || state != 0)) {
+ nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
+ "Failed to create 'environ' variable");
+ return NXT_UNIT_ERROR;
+ }
+ }
+
+ return NXT_UNIT_OK;
+}
+
+
+static void
+nxt_ruby_join_threads(nxt_unit_ctx_t *ctx, nxt_ruby_app_conf_t *c)
+{
+ uint32_t i;
+ nxt_ruby_ctx_t *rctx;
+
+ if (nxt_ruby_ctxs == NULL) {
+ return;
+ }
+
+ for (i = 0; i < c->threads - 1; i++) {
+ rctx = &nxt_ruby_ctxs[i];
+
+ if (rctx->thread != Qnil) {
+ rb_funcall(rctx->thread, rb_intern("join"), 0);
+
+ nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1));
+
+ } else {
+ nxt_unit_debug(ctx, "thread #%d not started", (int) (i + 1));
+ }
+ }
+
+ for (i = 0; i < c->threads - 1; i++) {
+ nxt_ruby_ctx_done(&nxt_ruby_ctxs[i]);
+ }
+
+ nxt_unit_free(ctx, nxt_ruby_ctxs);
+}
diff --git a/src/ruby/nxt_ruby.h b/src/ruby/nxt_ruby.h
index 77a65894..26430021 100644
--- a/src/ruby/nxt_ruby.h
+++ b/src/ruby/nxt_ruby.h
@@ -21,9 +21,13 @@
typedef struct {
- nxt_unit_ctx_t *unit_ctx;
+ VALUE env;
+ VALUE io_input;
+ VALUE io_error;
+ VALUE thread;
+ nxt_unit_ctx_t *ctx;
nxt_unit_request_info_t *req;
-} nxt_ruby_run_ctx_t;
+} nxt_ruby_ctx_t;
VALUE nxt_ruby_stream_io_input_init(void);
diff --git a/src/ruby/nxt_ruby_stream_io.c b/src/ruby/nxt_ruby_stream_io.c
index cc110035..69bf289e 100644
--- a/src/ruby/nxt_ruby_stream_io.c
+++ b/src/ruby/nxt_ruby_stream_io.c
@@ -8,7 +8,7 @@
#include <nxt_unit.h>
-static VALUE nxt_ruby_stream_io_new(VALUE class, VALUE wrap);
+static VALUE nxt_ruby_stream_io_new(VALUE class, VALUE arg);
static VALUE nxt_ruby_stream_io_initialize(int argc, VALUE *argv, VALUE self);
static VALUE nxt_ruby_stream_io_gets(VALUE obj);
static VALUE nxt_ruby_stream_io_each(VALUE obj);
@@ -16,8 +16,7 @@ static VALUE nxt_ruby_stream_io_read(VALUE obj, VALUE args);
static VALUE nxt_ruby_stream_io_rewind(VALUE obj);
static VALUE nxt_ruby_stream_io_puts(VALUE obj, VALUE args);
static VALUE nxt_ruby_stream_io_write(VALUE obj, VALUE args);
-nxt_inline long nxt_ruby_stream_io_s_write(nxt_ruby_run_ctx_t *run_ctx,
- VALUE val);
+nxt_inline long nxt_ruby_stream_io_s_write(nxt_ruby_ctx_t *rctx, VALUE val);
static VALUE nxt_ruby_stream_io_flush(VALUE obj);
@@ -63,13 +62,11 @@ nxt_ruby_stream_io_error_init(void)
static VALUE
-nxt_ruby_stream_io_new(VALUE class, VALUE wrap)
+nxt_ruby_stream_io_new(VALUE class, VALUE arg)
{
- VALUE self;
- nxt_ruby_run_ctx_t *run_ctx;
+ VALUE self;
- Data_Get_Struct(wrap, nxt_ruby_run_ctx_t, run_ctx);
- self = Data_Wrap_Struct(class, 0, 0, run_ctx);
+ self = Data_Wrap_Struct(class, 0, 0, (void *) (uintptr_t) arg);
rb_obj_call_init(self, 0, NULL);
@@ -89,12 +86,11 @@ nxt_ruby_stream_io_gets(VALUE obj)
{
VALUE buf;
ssize_t res;
- nxt_ruby_run_ctx_t *run_ctx;
+ nxt_ruby_ctx_t *rctx;
nxt_unit_request_info_t *req;
- Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx);
-
- req = run_ctx->req;
+ Data_Get_Struct(obj, nxt_ruby_ctx_t, rctx);
+ req = rctx->req;
if (req->content_length == 0) {
return Qnil;
@@ -145,13 +141,13 @@ nxt_ruby_stream_io_each(VALUE obj)
static VALUE
nxt_ruby_stream_io_read(VALUE obj, VALUE args)
{
- VALUE buf;
- long copy_size, u_size;
- nxt_ruby_run_ctx_t *run_ctx;
+ VALUE buf;
+ long copy_size, u_size;
+ nxt_ruby_ctx_t *rctx;
- Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx);
+ Data_Get_Struct(obj, nxt_ruby_ctx_t, rctx);
- copy_size = run_ctx->req->content_length;
+ copy_size = rctx->req->content_length;
if (RARRAY_LEN(args) > 0 && TYPE(RARRAY_PTR(args)[0]) == T_FIXNUM) {
u_size = NUM2LONG(RARRAY_PTR(args)[0]);
@@ -175,8 +171,7 @@ nxt_ruby_stream_io_read(VALUE obj, VALUE args)
return Qnil;
}
- copy_size = nxt_unit_request_read(run_ctx->req, RSTRING_PTR(buf),
- copy_size);
+ copy_size = nxt_unit_request_read(rctx->req, RSTRING_PTR(buf), copy_size);
if (RARRAY_LEN(args) > 1 && TYPE(RARRAY_PTR(args)[1]) == T_STRING) {
@@ -200,15 +195,15 @@ nxt_ruby_stream_io_rewind(VALUE obj)
static VALUE
nxt_ruby_stream_io_puts(VALUE obj, VALUE args)
{
- nxt_ruby_run_ctx_t *run_ctx;
+ nxt_ruby_ctx_t *rctx;
if (RARRAY_LEN(args) != 1) {
return Qnil;
}
- Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx);
+ Data_Get_Struct(obj, nxt_ruby_ctx_t, rctx);
- nxt_ruby_stream_io_s_write(run_ctx, RARRAY_PTR(args)[0]);
+ nxt_ruby_stream_io_s_write(rctx, RARRAY_PTR(args)[0]);
return Qnil;
}
@@ -217,23 +212,23 @@ nxt_ruby_stream_io_puts(VALUE obj, VALUE args)
static VALUE
nxt_ruby_stream_io_write(VALUE obj, VALUE args)
{
- long len;
- nxt_ruby_run_ctx_t *run_ctx;
+ long len;
+ nxt_ruby_ctx_t *rctx;
if (RARRAY_LEN(args) != 1) {
return Qnil;
}
- Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx);
+ Data_Get_Struct(obj, nxt_ruby_ctx_t, rctx);
- len = nxt_ruby_stream_io_s_write(run_ctx, RARRAY_PTR(args)[0]);
+ len = nxt_ruby_stream_io_s_write(rctx, RARRAY_PTR(args)[0]);
return LONG2FIX(len);
}
nxt_inline long
-nxt_ruby_stream_io_s_write(nxt_ruby_run_ctx_t *run_ctx, VALUE val)
+nxt_ruby_stream_io_s_write(nxt_ruby_ctx_t *rctx, VALUE val)
{
if (nxt_slow_path(val == Qnil)) {
return 0;
@@ -247,7 +242,7 @@ nxt_ruby_stream_io_s_write(nxt_ruby_run_ctx_t *run_ctx, VALUE val)
}
}
- nxt_unit_req_error(run_ctx->req, "Ruby: %s", RSTRING_PTR(val));
+ nxt_unit_req_error(rctx->req, "Ruby: %s", RSTRING_PTR(val));
return RSTRING_LEN(val);
}
diff --git a/src/test/nxt_http_parse_test.c b/src/test/nxt_http_parse_test.c
index 9630b21c..540309c1 100644
--- a/src/test/nxt_http_parse_test.c
+++ b/src/test/nxt_http_parse_test.c
@@ -23,9 +23,15 @@ typedef struct {
} nxt_http_parse_test_request_line_t;
+typedef struct {
+ nxt_int_t result;
+ unsigned discard_unsafe_fields:1;
+} nxt_http_parse_test_fields_t;
+
+
typedef union {
void *pointer;
- nxt_int_t result;
+ nxt_http_parse_test_fields_t fields;
nxt_http_parse_test_request_line_t request_line;
} nxt_http_parse_test_data_t;
@@ -324,10 +330,11 @@ static nxt_http_parse_test_case_t nxt_http_test_cases[] = {
{
nxt_string("GET / HTTP/1.1\r\n"
"X-Unknown-Header: value\r\n"
- "X-Good-Header: value\r\n\r\n"),
+ "X-Good-Header: value\r\n"
+ "!#$%&'*+.^_`|~: skipped\r\n\r\n"),
NXT_DONE,
&nxt_http_parse_test_fields,
- { .result = NXT_OK }
+ { .fields = { NXT_OK, 1 } }
},
{
nxt_string("GET / HTTP/1.1\r\n"
@@ -336,7 +343,14 @@ static nxt_http_parse_test_case_t nxt_http_test_cases[] = {
"X-Bad-Header: value\r\n\r\n"),
NXT_DONE,
&nxt_http_parse_test_fields,
- { .result = NXT_ERROR }
+ { .fields = { NXT_ERROR, 1 } }
+ },
+ {
+ nxt_string("GET / HTTP/1.1\r\n"
+ "!#$%&'*+.^_`|~: allowed\r\n\r\n"),
+ NXT_DONE,
+ &nxt_http_parse_test_fields,
+ { .fields = { NXT_ERROR, 0 } }
},
};
@@ -349,6 +363,10 @@ static nxt_http_field_proc_t nxt_http_test_fields[] = {
{ nxt_string("X-Good-Header"),
&nxt_http_test_header_return,
NXT_OK },
+
+ { nxt_string("!#$%&'*+.^_`|~"),
+ &nxt_http_test_header_return,
+ NXT_ERROR },
};
@@ -540,6 +558,10 @@ nxt_http_parse_test(nxt_thread_t *thr)
return NXT_ERROR;
}
+ if (test->handler == &nxt_http_parse_test_fields) {
+ rp.discard_unsafe_fields = test->data.fields.discard_unsafe_fields;
+ }
+
rc = nxt_http_parse_test_run(&rp, &test->request);
if (rc != test->result) {
@@ -740,7 +762,7 @@ nxt_http_parse_test_request_line(nxt_http_request_parse_t *rp,
return NXT_ERROR;
}
- if (rp->complex_target != test->complex_target) {
+ if (rp->complex_target != (test->complex_target | test->quoted_target)) {
nxt_log_alert(log, "http parse test case failed:\n"
" - request:\n\"%V\"\n"
" - complex_target: %d (expected: %d)",
@@ -748,6 +770,7 @@ nxt_http_parse_test_request_line(nxt_http_request_parse_t *rp,
return NXT_ERROR;
}
+#if 0
if (rp->quoted_target != test->quoted_target) {
nxt_log_alert(log, "http parse test case failed:\n"
" - request:\n\"%V\"\n"
@@ -763,6 +786,7 @@ nxt_http_parse_test_request_line(nxt_http_request_parse_t *rp,
request, rp->space_in_target, test->space_in_target);
return NXT_ERROR;
}
+#endif
return NXT_OK;
}
@@ -776,11 +800,11 @@ nxt_http_parse_test_fields(nxt_http_request_parse_t *rp,
rc = nxt_http_fields_process(rp->fields, &nxt_http_test_fields_hash, NULL);
- if (rc != data->result) {
+ if (rc != data->fields.result) {
nxt_log_alert(log, "http parse test hash failed:\n"
" - request:\n\"%V\"\n"
" - result: %i (expected: %i)",
- request, rc, data->result);
+ request, rc, data->fields.result);
return NXT_ERROR;
}
diff --git a/src/test/nxt_unit_app_test.c b/src/test/nxt_unit_app_test.c
index 4ecb8d6b..a5f3728c 100644
--- a/src/test/nxt_unit_app_test.c
+++ b/src/test/nxt_unit_app_test.c
@@ -6,6 +6,9 @@
#include <nxt_unit.h>
#include <nxt_unit_request.h>
#include <nxt_clang.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdlib.h>
#define CONTENT_TYPE "Content-Type"
@@ -28,12 +31,112 @@
#define BODY " Body:\n"
-static inline char *
-copy(char *p, const void *src, uint32_t len)
+static int ready_handler(nxt_unit_ctx_t *ctx);
+static void *worker(void *main_ctx);
+static void greeting_app_request_handler(nxt_unit_request_info_t *req);
+static inline char *copy(char *p, const void *src, uint32_t len);
+
+
+static int thread_count;
+static pthread_t *threads;
+
+
+int
+main(int argc, char **argv)
{
- memcpy(p, src, len);
+ int i, err;
+ nxt_unit_ctx_t *ctx;
+ nxt_unit_init_t init;
- return p + len;
+ if (argc == 3 && strcmp(argv[1], "-t") == 0) {
+ thread_count = atoi(argv[2]);
+ }
+
+ memset(&init, 0, sizeof(nxt_unit_init_t));
+
+ init.callbacks.request_handler = greeting_app_request_handler;
+ init.callbacks.ready_handler = ready_handler;
+
+ ctx = nxt_unit_init(&init);
+ if (ctx == NULL) {
+ return 1;
+ }
+
+ err = nxt_unit_run(ctx);
+
+ nxt_unit_debug(ctx, "main worker finished with %d code", err);
+
+ if (thread_count > 1) {
+ for (i = 0; i < thread_count - 1; i++) {
+ err = pthread_join(threads[i], NULL);
+
+ if (nxt_fast_path(err == 0)) {
+ nxt_unit_debug(ctx, "join thread #%d", i);
+
+ } else {
+ nxt_unit_alert(ctx, "pthread_join(#%d) failed: %s (%d)",
+ i, strerror(err), err);
+ }
+ }
+
+ nxt_unit_free(ctx, threads);
+ }
+
+ nxt_unit_done(ctx);
+
+ nxt_unit_debug(NULL, "main worker done");
+
+ return 0;
+}
+
+
+static int
+ready_handler(nxt_unit_ctx_t *ctx)
+{
+ int i, err;
+
+ nxt_unit_debug(ctx, "ready");
+
+ if (!nxt_unit_is_main_ctx(ctx) || thread_count <= 1) {
+ return NXT_UNIT_OK;
+ }
+
+ threads = nxt_unit_malloc(ctx, sizeof(pthread_t) * (thread_count - 1));
+ if (threads == NULL) {
+ return NXT_UNIT_ERROR;
+ }
+
+ for (i = 0; i < thread_count - 1; i++) {
+ err = pthread_create(&threads[i], NULL, worker, ctx);
+ if (err != 0) {
+ return NXT_UNIT_ERROR;
+ }
+ }
+
+ return NXT_UNIT_OK;
+}
+
+
+static void *
+worker(void *main_ctx)
+{
+ int rc;
+ nxt_unit_ctx_t *ctx;
+
+ ctx = nxt_unit_ctx_alloc(main_ctx, NULL);
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ nxt_unit_debug(ctx, "start worker");
+
+ rc = nxt_unit_run(ctx);
+
+ nxt_unit_debug(ctx, "worker finished with %d code", rc);
+
+ nxt_unit_done(ctx);
+
+ return (void *) (intptr_t) rc;
}
@@ -168,24 +271,11 @@ fail:
nxt_unit_request_done(req, rc);
}
-int
-main()
-{
- nxt_unit_ctx_t *ctx;
- nxt_unit_init_t init;
-
- memset(&init, 0, sizeof(nxt_unit_init_t));
-
- init.callbacks.request_handler = greeting_app_request_handler;
- ctx = nxt_unit_init(&init);
- if (ctx == NULL) {
- return 1;
- }
-
- nxt_unit_run(ctx);
-
- nxt_unit_done(ctx);
+static inline char *
+copy(char *p, const void *src, uint32_t len)
+{
+ memcpy(p, src, len);
- return 0;
+ return p + len;
}
diff --git a/src/test/nxt_unit_websocket_chat.c b/src/test/nxt_unit_websocket_chat.c
index 6e274722..39f8a440 100644
--- a/src/test/nxt_unit_websocket_chat.c
+++ b/src/test/nxt_unit_websocket_chat.c
@@ -30,7 +30,7 @@ typedef struct {
static int ws_chat_root(nxt_unit_request_info_t *req);
-static void ws_chat_broadcast(const void *buf, size_t size);
+static void ws_chat_broadcast(const char *buf, size_t size);
static const char ws_chat_index_html[];
@@ -139,18 +139,18 @@ ws_chat_root(nxt_unit_request_info_t *req)
static void
-ws_chat_broadcast(const void *buf, size_t size)
+ws_chat_broadcast(const char *buf, size_t size)
{
ws_chat_request_data_t *data;
nxt_unit_request_info_t *req;
- nxt_unit_debug(NULL, "broadcast: %s", buf);
+ nxt_unit_debug(NULL, "broadcast: %*.s", (int) size, buf);
nxt_queue_each(data, &ws_chat_sessions, ws_chat_request_data_t, link) {
req = nxt_unit_get_request_info_from_data(data);
- nxt_unit_req_debug(req, "broadcast: %s", buf);
+ nxt_unit_req_debug(req, "send: %*.s", (int) size, buf);
nxt_unit_websocket_send(req, NXT_WEBSOCKET_OP_TEXT, 1, buf, size);
} nxt_queue_loop;