diff options
author | Valentin Bartenev <vbart@nginx.com> | 2020-05-14 13:15:01 +0300 |
---|---|---|
committer | Valentin Bartenev <vbart@nginx.com> | 2020-05-14 13:15:01 +0300 |
commit | 376d758dd72ac27f2bd5bb833ba68f5c9b531880 (patch) | |
tree | a3891a0586669543c35004ccbff3a6deca893903 | |
parent | 0174c971b5ec0d604e4e9becfa41e0bc31179e57 (diff) | |
download | unit-376d758dd72ac27f2bd5bb833ba68f5c9b531880.tar.gz unit-376d758dd72ac27f2bd5bb833ba68f5c9b531880.tar.bz2 |
PHP: implemented "targets" option.
This allows to specify multiple subsequent targets inside PHP applications.
For example:
{
"listeners": {
"*:80": {
"pass": "routes"
}
},
"routes": [
{
"match": {
"uri": "/info"
},
"action": {
"pass": "applications/my_app/phpinfo"
}
},
{
"match": {
"uri": "/hello"
},
"action": {
"pass": "applications/my_app/hello"
}
},
{
"action": {
"pass": "applications/my_app/rest"
}
}
],
"applications": {
"my_app": {
"type": "php",
"targets": {
"phpinfo": {
"script": "phpinfo.php",
"root": "/www/data/admin",
},
"hello": {
"script": "hello.php",
"root": "/www/data/test",
},
"rest": {
"root": "/www/data/example.com",
"index": "index.php"
},
}
}
}
}
-rw-r--r-- | src/nxt_application.h | 6 | ||||
-rw-r--r-- | src/nxt_conf.h | 2 | ||||
-rw-r--r-- | src/nxt_conf_validation.c | 202 | ||||
-rw-r--r-- | src/nxt_http.h | 2 | ||||
-rw-r--r-- | src/nxt_http_request.c | 2 | ||||
-rw-r--r-- | src/nxt_http_route.c | 21 | ||||
-rw-r--r-- | src/nxt_main_process.c | 20 | ||||
-rw-r--r-- | src/nxt_php_sapi.c | 446 | ||||
-rw-r--r-- | src/nxt_router.c | 64 | ||||
-rw-r--r-- | src/nxt_router.h | 3 | ||||
-rw-r--r-- | src/nxt_unit_request.h | 1 |
11 files changed, 509 insertions, 260 deletions
diff --git a/src/nxt_application.h b/src/nxt_application.h index e7177887..972a712b 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -54,9 +54,7 @@ typedef struct { typedef struct { - char *root; - nxt_str_t script; - nxt_str_t index; + nxt_conf_value_t *targets; nxt_conf_value_t *options; } nxt_php_app_conf_t; @@ -101,6 +99,8 @@ struct nxt_common_app_conf_s { nxt_ruby_app_conf_t ruby; nxt_java_app_conf_t java; } u; + + nxt_conf_value_t *self; }; diff --git a/src/nxt_conf.h b/src/nxt_conf.h index 201a3a14..149af39a 100644 --- a/src/nxt_conf.h +++ b/src/nxt_conf.h @@ -118,7 +118,7 @@ NXT_EXPORT double nxt_conf_get_number(nxt_conf_value_t *value); NXT_EXPORT uint8_t nxt_conf_get_boolean(nxt_conf_value_t *value); // FIXME reimplement and reorder functions below -nxt_uint_t nxt_conf_object_members_count(nxt_conf_value_t *value); +NXT_EXPORT nxt_uint_t nxt_conf_object_members_count(nxt_conf_value_t *value); nxt_conf_value_t *nxt_conf_create_object(nxt_mp_t *mp, nxt_uint_t count); void nxt_conf_set_member(nxt_conf_value_t *object, nxt_str_t *name, nxt_conf_value_t *value, uint32_t index); diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 476fc97b..0f46560d 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -23,13 +23,24 @@ typedef enum { NXT_CONF_VLDT_OBJECT = 1 << NXT_CONF_OBJECT, } nxt_conf_vldt_type_t; +#define NXT_CONF_VLDT_ANY_TYPE (NXT_CONF_VLDT_NULL \ + |NXT_CONF_VLDT_BOOLEAN \ + |NXT_CONF_VLDT_NUMBER \ + |NXT_CONF_VLDT_STRING \ + |NXT_CONF_VLDT_ARRAY \ + |NXT_CONF_VLDT_OBJECT) + + +typedef nxt_int_t (*nxt_conf_vldt_handler_t)(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, + void *data); + typedef struct { - nxt_str_t name; - nxt_conf_vldt_type_t type; - nxt_int_t (*validator)(nxt_conf_validation_t *vldt, - nxt_conf_value_t *value, void *data); - void *data; + nxt_str_t name; + nxt_conf_vldt_type_t type; + nxt_conf_vldt_handler_t validator; + void *data; } nxt_conf_vldt_object_t; @@ -106,6 +117,14 @@ static nxt_int_t nxt_conf_vldt_environment(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_argument(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_php(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_php_targets_exclusive( + nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_php_targets(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_php_target(nxt_conf_validation_t *vldt, + nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_php_option(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_java_classpath(nxt_conf_validation_t *vldt, @@ -630,6 +649,24 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { }; +static nxt_conf_vldt_object_t nxt_conf_vldt_php_target_members[] = { + { nxt_string("root"), + NXT_CONF_VLDT_STRING, + NULL, + NULL }, + + { nxt_string("script"), + NXT_CONF_VLDT_STRING, + NULL, + NULL }, + + { nxt_string("index"), + NXT_CONF_VLDT_STRING, + NULL, + NULL } +}; + + static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[] = { { nxt_string("file"), NXT_CONF_VLDT_STRING, @@ -650,7 +687,17 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[] = { }; -static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { +static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[] = { + { nxt_string("options"), + NXT_CONF_VLDT_OBJECT, + &nxt_conf_vldt_object, + (void *) &nxt_conf_vldt_php_options_members }, + + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) +}; + + +static nxt_conf_vldt_object_t nxt_conf_vldt_php_notargets_members[] = { { nxt_string("root"), NXT_CONF_VLDT_STRING, NULL, @@ -666,12 +713,32 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { NULL, NULL }, - { nxt_string("options"), + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members) +}; + + +static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { + { nxt_string("root"), + NXT_CONF_VLDT_ANY_TYPE, + &nxt_conf_vldt_php_targets_exclusive, + (void *) "root" }, + + { nxt_string("script"), + NXT_CONF_VLDT_ANY_TYPE, + &nxt_conf_vldt_php_targets_exclusive, + (void *) "script" }, + + { nxt_string("index"), + NXT_CONF_VLDT_ANY_TYPE, + &nxt_conf_vldt_php_targets_exclusive, + (void *) "index" }, + + { nxt_string("targets"), NXT_CONF_VLDT_OBJECT, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_php_options_members }, + &nxt_conf_vldt_php_targets, + NULL }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members) }; @@ -755,7 +822,7 @@ nxt_conf_validate(nxt_conf_validation_t *vldt) } -#define NXT_CONF_VLDT_ANY_TYPE \ +#define NXT_CONF_VLDT_ANY_TYPE_STR \ "either a null, a boolean, an integer, " \ "a number, a string, an array, or an object" @@ -768,7 +835,7 @@ nxt_conf_vldt_type(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_str_t expected; nxt_bool_t serial; nxt_uint_t value_type, n, t; - u_char buf[nxt_length(NXT_CONF_VLDT_ANY_TYPE)]; + u_char buf[nxt_length(NXT_CONF_VLDT_ANY_TYPE_STR)]; static nxt_str_t type_name[] = { nxt_string("a null"), @@ -1034,11 +1101,13 @@ nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, { nxt_str_t pass; nxt_int_t ret; - nxt_str_t segments[2]; + nxt_str_t segments[3]; + + static nxt_str_t targets_str = nxt_string("targets"); nxt_conf_get_string(value, &pass); - ret = nxt_http_pass_segments(vldt->pool, &pass, segments, 2); + ret = nxt_http_pass_segments(vldt->pool, &pass, segments, 3); if (ret != NXT_OK) { if (ret == NXT_DECLINED) { @@ -1067,12 +1136,26 @@ nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, goto error; } + if (segments[2].length > 0) { + value = nxt_conf_get_object_member(value, &targets_str, NULL); + + if (value == NULL) { + goto error; + } + + value = nxt_conf_get_object_member(value, &segments[2], NULL); + + if (value == NULL) { + goto error; + } + } + return NXT_OK; } if (nxt_str_eq(&segments[0], "upstreams", 9)) { - if (segments[1].length == 0) { + if (segments[1].length == 0 || segments[2].length != 0) { goto error; } @@ -1092,6 +1175,11 @@ nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, } if (nxt_str_eq(&segments[0], "routes", 6)) { + + if (segments[2].length != 0) { + goto error; + } + value = nxt_conf_get_object_member(vldt->conf, &segments[0], NULL); if (value == NULL) { @@ -1482,13 +1570,17 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name, static nxt_str_t type_str = nxt_string("type"); - static void *members[] = { - nxt_conf_vldt_external_members, - nxt_conf_vldt_python_members, - nxt_conf_vldt_php_members, - nxt_conf_vldt_perl_members, - nxt_conf_vldt_ruby_members, - nxt_conf_vldt_java_members, + static struct { + nxt_conf_vldt_handler_t validator; + nxt_conf_vldt_object_t *members; + + } types[] = { + { nxt_conf_vldt_object, nxt_conf_vldt_external_members }, + { nxt_conf_vldt_object, nxt_conf_vldt_python_members }, + { nxt_conf_vldt_php, NULL }, + { nxt_conf_vldt_object, nxt_conf_vldt_perl_members }, + { nxt_conf_vldt_object, nxt_conf_vldt_ruby_members }, + { nxt_conf_vldt_object, nxt_conf_vldt_java_members }, }; ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT); @@ -1522,7 +1614,7 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name, &type); } - return nxt_conf_vldt_object(vldt, value, members[lang->type]); + return types[lang->type].validator(vldt, value, types[lang->type].members); } @@ -1934,6 +2026,70 @@ nxt_conf_vldt_argument(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) static nxt_int_t +nxt_conf_vldt_php(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + nxt_conf_value_t *targets; + + static nxt_str_t targets_str = nxt_string("targets"); + + targets = nxt_conf_get_object_member(value, &targets_str, NULL); + + if (targets != NULL) { + return nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_php_members); + } + + return nxt_conf_vldt_object(vldt, value, + nxt_conf_vldt_php_notargets_members); +} + + +static nxt_int_t +nxt_conf_vldt_php_targets_exclusive(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + return nxt_conf_vldt_error(vldt, "The \"%s\" option is mutually exclusive " + "with the \"targets\" object.", data); +} + + +static nxt_int_t +nxt_conf_vldt_php_targets(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + nxt_uint_t n; + + n = nxt_conf_object_members_count(value); + + if (n > 254) { + return nxt_conf_vldt_error(vldt, "The \"targets\" object must not " + "contain more than 254 members."); + } + + return nxt_conf_vldt_object_iterator(vldt, value, + &nxt_conf_vldt_php_target); +} + + +static nxt_int_t +nxt_conf_vldt_php_target(nxt_conf_validation_t *vldt, nxt_str_t *name, + nxt_conf_value_t *value) +{ + if (name->length == 0) { + return nxt_conf_vldt_error(vldt, + "The PHP target name must not be empty."); + } + + if (nxt_conf_type(value) != NXT_CONF_OBJECT) { + return nxt_conf_vldt_error(vldt, "The \"%V\" PHP target must be " + "an object.", name); + } + + return nxt_conf_vldt_object(vldt, value, &nxt_conf_vldt_php_target_members); +} + + +static nxt_int_t nxt_conf_vldt_php_option(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value) { diff --git a/src/nxt_http.h b/src/nxt_http.h index 6f593cd2..68051e69 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -175,6 +175,7 @@ struct nxt_http_request_s { nxt_http_status_t status:16; uint8_t pass_count; /* 8 bits */ + uint8_t app_target; nxt_http_protocol_t protocol:8; /* 2 bits */ uint8_t logged; /* 1 bit */ uint8_t header_sent; /* 1 bit */ @@ -201,6 +202,7 @@ struct nxt_http_action_s { } u; nxt_str_t name; + nxt_int_t target; }; diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 050587f7..cc1ae17d 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -341,6 +341,8 @@ nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_str_set(&r->server_name, "localhost"); } + r->app_target = action->target; + nxt_router_process_http_request(task, r, action->u.application); return NULL; diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index bb6e9dc9..06bc91da 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -1151,8 +1151,10 @@ static nxt_int_t nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action) { - nxt_int_t ret; - nxt_str_t segments[2]; + nxt_str_t *targets; + nxt_int_t ret; + nxt_uint_t i; + nxt_str_t segments[3]; if (action->handler != NULL) { if (action->handler == nxt_http_static_handler @@ -1164,7 +1166,7 @@ nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NXT_OK; } - ret = nxt_http_pass_segments(tmcf->mem_pool, &action->name, segments, 2); + ret = nxt_http_pass_segments(tmcf->mem_pool, &action->name, segments, 3); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } @@ -1173,6 +1175,17 @@ nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_router_listener_application(tmcf, &segments[1], action); nxt_router_app_use(task, action->u.application, 1); + if (segments[2].length != 0) { + targets = action->u.application->targets; + + for (i = 0; !nxt_strstr_eq(&segments[2], &targets[i]); i++); + + action->target = i; + + } else { + action->target = 0; + } + } else if (nxt_str_eq(&segments[0], "upstreams", 9)) { nxt_upstream_find(tmcf->router_conf->upstreams, &segments[1], action); @@ -1298,6 +1311,8 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_router_listener_application(tmcf, name, action); nxt_router_app_use(task, action->u.application, 1); + action->target = 0; + return action; } diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index eed37752..c35954c0 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -275,21 +275,9 @@ static nxt_conf_map_t nxt_python_app_conf[] = { static nxt_conf_map_t nxt_php_app_conf[] = { { - nxt_string("root"), - NXT_CONF_MAP_CSTRZ, - offsetof(nxt_common_app_conf_t, u.php.root), - }, - - { - nxt_string("script"), - NXT_CONF_MAP_STR, - offsetof(nxt_common_app_conf_t, u.php.script), - }, - - { - nxt_string("index"), - NXT_CONF_MAP_STR, - offsetof(nxt_common_app_conf_t, u.php.index), + nxt_string("targets"), + NXT_CONF_MAP_PTR, + offsetof(nxt_common_app_conf_t, u.php.targets), }, { @@ -457,6 +445,8 @@ nxt_port_main_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) } } + app_conf.self = conf; + ret = nxt_main_start_worker_process(task, task->thread->runtime, &app_conf, msg->port_msg.stream); diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index f5053652..ccbdd475 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -29,8 +29,20 @@ #define NXT_PHP7 1 #endif + +typedef struct { + nxt_str_t root; + nxt_str_t index; + nxt_str_t script_name; + nxt_str_t script_dirname; + nxt_str_t script_filename; +} nxt_php_target_t; + + typedef struct { char *cookie; + nxt_str_t *root; + nxt_str_t *index; nxt_str_t path_info; nxt_str_t script_name; nxt_str_t script_filename; @@ -53,26 +65,27 @@ typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS); static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf); +static nxt_int_t nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target, + nxt_conf_value_t *conf); +static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, + int type); +static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, + int type); +static void nxt_php_disable(nxt_task_t *task, const char *type, + nxt_str_t *value, char **ptr, nxt_php_disable_t disable); +static nxt_int_t nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir); static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t); static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t); -static nxt_int_t nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir); nxt_inline u_char *nxt_realpath(const void *c); -nxt_inline void nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, - const nxt_str_t *dirname); -static void nxt_php_script_request_handler(nxt_unit_request_info_t *req); -static void nxt_php_path_request_handler(nxt_unit_request_info_t *req); -static nxt_int_t nxt_php_request_init(nxt_php_run_ctx_t *ctx, +static void nxt_php_request_handler(nxt_unit_request_info_t *req); +static void 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); +nxt_inline void nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir); static int nxt_php_startup(sapi_module_struct *sapi_module); -static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, - int type); -static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, - int type); -static void nxt_php_disable(nxt_task_t *task, const char *type, - nxt_str_t *value, char **ptr, nxt_php_disable_t disable); static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC); static void *nxt_php_hash_str_find_ptr(const HashTable *ht, const nxt_str_t *str); @@ -210,13 +223,6 @@ static sapi_module_struct nxt_php_sapi_module = }; -static nxt_str_t nxt_php_root; -static nxt_str_t nxt_php_script_name; -static nxt_str_t nxt_php_script_dirname; -static nxt_str_t nxt_php_script_filename; -static nxt_str_t nxt_php_index = nxt_string("index.php"); - - static uint32_t compat[] = { NXT_VERNUM, NXT_DEBUG, }; @@ -232,6 +238,9 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { }; +static nxt_php_target_t *nxt_php_targets; +static nxt_int_t nxt_php_last_target = -1; + static nxt_task_t *nxt_php_task; #if defined(ZTS) && PHP_VERSION_ID < 70400 static void ***tsrm_ls; @@ -241,11 +250,11 @@ static void ***tsrm_ls; static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) { - u_char *p, *tmp; - nxt_str_t ini_path; - nxt_str_t *root, *script_filename, *script_dirname, *script_name; - nxt_str_t *index; + u_char *p; + uint32_t next; + nxt_str_t ini_path, name; nxt_int_t ret; + nxt_uint_t n; nxt_port_t *my_port, *main_port; nxt_runtime_t *rt; nxt_unit_ctx_t *unit_ctx; @@ -261,105 +270,33 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) c = &conf->u.php; - if (c->root == NULL) { - nxt_alert(task, "php root is empty"); - return NXT_ERROR; - } - - root = &nxt_php_root; - script_filename = &nxt_php_script_filename; - script_dirname = &nxt_php_script_dirname; - script_name = &nxt_php_script_name; - index = &nxt_php_index; + n = (c->targets != NULL) ? nxt_conf_object_members_count(c->targets) : 1; - root->start = nxt_realpath(c->root); - if (nxt_slow_path(root->start == NULL)) { - nxt_alert(task, "root realpath(%s) failed %E", c->root, nxt_errno); + nxt_php_targets = nxt_zalloc(sizeof(nxt_php_target_t) * n); + if (nxt_slow_path(nxt_php_targets == NULL)) { return NXT_ERROR; } - root->length = nxt_strlen(root->start); - - nxt_php_str_trim_trail(root, '/'); - - if (c->script.length > 0) { - nxt_php_str_trim_lead(&c->script, '/'); - - tmp = nxt_malloc(root->length + 1 + c->script.length + 1); - if (nxt_slow_path(tmp == NULL)) { - return NXT_ERROR; - } - - p = tmp; - - p = nxt_cpymem(p, root->start, root->length); - *p++ = '/'; - - p = nxt_cpymem(p, c->script.start, c->script.length); - *p = '\0'; - - script_filename->start = nxt_realpath(tmp); - if (nxt_slow_path(script_filename->start == NULL)) { - nxt_alert(task, "script realpath(%s) failed %E", tmp, nxt_errno); - return NXT_ERROR; - } - - nxt_free(tmp); - - script_filename->length = nxt_strlen(script_filename->start); - - if (!nxt_str_start(script_filename, root->start, root->length)) { - nxt_alert(task, "script is not under php root"); - return NXT_ERROR; - } + if (c->targets != NULL) { + next = 0; - ret = nxt_php_dirname(script_filename, script_dirname); - if (nxt_slow_path(ret != NXT_OK)) { - return NXT_ERROR; - } + for (n = 0; /* void */; n++) { + value = nxt_conf_next_object_member(c->targets, &name, &next); + if (value == NULL) { + break; + } - script_name->length = c->script.length + 1; - script_name->start = nxt_malloc(script_name->length); - if (nxt_slow_path(script_name->start == NULL)) { - return NXT_ERROR; + ret = nxt_php_set_target(task, &nxt_php_targets[n], value); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } } - script_name->start[0] = '/'; - nxt_memcpy(script_name->start + 1, c->script.start, c->script.length); - - nxt_log_error(NXT_LOG_INFO, task->log, - "(ABS_MODE) php script \"%V\" root: \"%V\"", - script_name, root); - } else { - nxt_log_error(NXT_LOG_INFO, task->log, - "(non ABS_MODE) php root: \"%V\"", root); - } - - if (c->index.length > 0) { - index->length = c->index.length; - index->start = nxt_malloc(index->length); - if (nxt_slow_path(index->start == NULL)) { - return NXT_ERROR; - } - - nxt_memcpy(index->start, c->index.start, c->index.length); - } - - nxt_memzero(&php_init, sizeof(nxt_unit_init_t)); - - if (nxt_php_script_filename.start != NULL) { - if (nxt_slow_path(chdir((char *) script_dirname->start) != 0)) { - nxt_alert(task, "failed to chdir(%V) %E", script_dirname, - nxt_errno); - + ret = nxt_php_set_target(task, &nxt_php_targets[0], conf->self); + if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } - - php_init.callbacks.request_handler = nxt_php_script_request_handler; - - } else { - php_init.callbacks.request_handler = nxt_php_path_request_handler; } #ifdef ZTS @@ -430,6 +367,10 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) return NXT_ERROR; } + nxt_memzero(&php_init, sizeof(nxt_unit_init_t)); + + php_init.callbacks.request_handler = nxt_php_request_handler; + php_init.ready_port.id.pid = main_port->pid; php_init.ready_port.id.id = main_port->id; php_init.ready_port.out_fd = main_port->pair[1]; @@ -462,6 +403,125 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) } +static nxt_int_t +nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target, + nxt_conf_value_t *conf) +{ + u_char *tmp, *p; + nxt_str_t str; + nxt_int_t ret; + nxt_conf_value_t *value; + + static nxt_str_t root_str = nxt_string("root"); + static nxt_str_t script_str = nxt_string("script"); + static nxt_str_t index_str = nxt_string("index"); + + value = nxt_conf_get_object_member(conf, &root_str, NULL); + + if (value == NULL) { + nxt_alert(task, "no php root specified"); + return NXT_ERROR; + } + + nxt_conf_get_string(value, &str); + + tmp = nxt_malloc(str.length + 1); + if (nxt_slow_path(tmp == NULL)) { + return NXT_ERROR; + } + + p = tmp; + + p = nxt_cpymem(p, str.start, str.length); + *p = '\0'; + + p = nxt_realpath(tmp); + if (nxt_slow_path(p == NULL)) { + nxt_alert(task, "root realpath(%s) failed %E", tmp, nxt_errno); + return NXT_ERROR; + } + + nxt_free(tmp); + + target->root.length = nxt_strlen(p); + target->root.start = p; + + nxt_php_str_trim_trail(&target->root, '/'); + + value = nxt_conf_get_object_member(conf, &script_str, NULL); + + if (value != NULL) { + nxt_conf_get_string(value, &str); + + nxt_php_str_trim_lead(&str, '/'); + + tmp = nxt_malloc(target->root.length + 1 + str.length + 1); + if (nxt_slow_path(tmp == NULL)) { + return NXT_ERROR; + } + + p = tmp; + + p = nxt_cpymem(p, target->root.start, target->root.length); + *p++ = '/'; + + p = nxt_cpymem(p, str.start, str.length); + *p = '\0'; + + p = nxt_realpath(tmp); + if (nxt_slow_path(p == NULL)) { + nxt_alert(task, "script realpath(%s) failed %E", tmp, nxt_errno); + return NXT_ERROR; + } + + nxt_free(tmp); + + target->script_filename.length = nxt_strlen(p); + target->script_filename.start = p; + + if (!nxt_str_start(&target->script_filename, + target->root.start, target->root.length)) + { + nxt_alert(task, "script is not under php root"); + return NXT_ERROR; + } + + ret = nxt_php_dirname(&target->script_filename, + &target->script_dirname); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + target->script_name.length = target->script_filename.length + - target->root.length; + target->script_name.start = target->script_filename.start + + target->root.length; + + } else { + value = nxt_conf_get_object_member(conf, &index_str, NULL); + + if (value != NULL) { + nxt_conf_get_string(value, &str); + + tmp = nxt_malloc(str.length); + if (nxt_slow_path(tmp == NULL)) { + return NXT_ERROR; + } + + nxt_memcpy(tmp, str.start, str.length); + + target->index.length = str.length; + target->index.start = tmp; + + } else { + nxt_str_set(&target->index, "index.php"); + } + } + + return NXT_OK; +} + + static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type) { @@ -686,56 +746,44 @@ nxt_realpath(const void *c) static void -nxt_php_script_request_handler(nxt_unit_request_info_t *req) +nxt_php_request_handler(nxt_unit_request_info_t *req) { - zend_file_handle file_handle; - nxt_php_run_ctx_t ctx; + nxt_php_target_t *target; + nxt_php_run_ctx_t ctx; + nxt_unit_request_t *r; + + r = req->request; + target = &nxt_php_targets[r->app_target]; nxt_memzero(&ctx, sizeof(ctx)); ctx.req = req; - ctx.script_filename = nxt_php_script_filename; - ctx.script_dirname = nxt_php_script_dirname; - ctx.script_name = nxt_php_script_name; - - nxt_memzero(&file_handle, sizeof(file_handle)); + ctx.root = &target->root; + ctx.index = &target->index; - file_handle.type = ZEND_HANDLE_FILENAME; - file_handle.filename = (char *) ctx.script_filename.start; - - if (nxt_slow_path(nxt_php_request_init(&ctx, req->request) != NXT_OK)) { - nxt_unit_request_done(req, NXT_UNIT_ERROR); + if (target->script_filename.length == 0) { + nxt_php_dynamic_request(&ctx, r); return; } - php_execute_script(&file_handle TSRMLS_CC); + ctx.script_filename = target->script_filename; + ctx.script_dirname = target->script_dirname; + ctx.script_name = target->script_name; - if (ctx.chdir) { - nxt_php_vcwd_chdir(ctx.req, &nxt_php_script_dirname); - } + ctx.chdir = (r->app_target != nxt_php_last_target); - php_request_shutdown(NULL); + nxt_php_execute(&ctx, r); - nxt_unit_request_done(req, NXT_UNIT_OK); + nxt_php_last_target = ctx.chdir ? -1 : r->app_target; } static void -nxt_php_path_request_handler(nxt_unit_request_info_t *req) +nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) { - u_char *p; - nxt_str_t path, script_name; - nxt_int_t ret; - zend_file_handle file_handle; - nxt_php_run_ctx_t run_ctx, *ctx; - nxt_unit_request_t *r; - - nxt_memzero(&run_ctx, sizeof(run_ctx)); - - ctx = &run_ctx; - ctx->req = req; - - r = req->request; + u_char *p; + nxt_str_t path, script_name; + nxt_int_t ret; path.length = r->path_length; path.start = nxt_unit_sptr_get(&r->path); @@ -750,26 +798,26 @@ nxt_php_path_request_handler(nxt_unit_request_info_t *req) ctx->path_info.length = r->path_length - path.length; } else if (path.start[path.length - 1] == '/') { - script_name = nxt_php_index; + script_name = *ctx->index; } else { if (nxt_slow_path(path.length < 4 || nxt_memcmp(path.start + (path.length - 4), ".php", 4))) { - nxt_unit_request_done(req, NXT_UNIT_ERROR); + nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); return; } } - ctx->script_filename.length = nxt_php_root.length + ctx->script_filename.length = ctx->root->length + path.length + script_name.length; p = nxt_malloc(ctx->script_filename.length + 1); if (nxt_slow_path(p == NULL)) { - nxt_unit_request_done(req, NXT_UNIT_ERROR); + nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); return; } @@ -777,9 +825,9 @@ nxt_php_path_request_handler(nxt_unit_request_info_t *req) ctx->script_filename.start = p; ctx->script_name.length = path.length + script_name.length; - ctx->script_name.start = p + nxt_php_root.length; + ctx->script_name.start = p + ctx->root->length; - p = nxt_cpymem(p, nxt_php_root.start, nxt_php_root.length); + p = nxt_cpymem(p, ctx->root->start, ctx->root->length); p = nxt_cpymem(p, path.start, path.length); if (script_name.length > 0) { @@ -788,50 +836,33 @@ nxt_php_path_request_handler(nxt_unit_request_info_t *req) *p = '\0'; - nxt_memzero(&file_handle, sizeof(file_handle)); - - file_handle.type = ZEND_HANDLE_FILENAME; - file_handle.filename = (char *) ctx->script_filename.start; + ctx->chdir = 1; ret = nxt_php_dirname(&ctx->script_filename, &ctx->script_dirname); if (nxt_slow_path(ret != NXT_OK)) { - nxt_unit_request_done(req, NXT_UNIT_ERROR); + nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); nxt_free(ctx->script_filename.start); return; } - if (nxt_slow_path(nxt_php_request_init(ctx, req->request) != NXT_OK)) { - nxt_unit_request_done(req, NXT_UNIT_ERROR); - goto cleanup; - } - - nxt_php_vcwd_chdir(ctx->req, &ctx->script_dirname); - - php_execute_script(&file_handle TSRMLS_CC); - - php_request_shutdown(NULL); - - nxt_unit_request_done(req, NXT_UNIT_OK); - -cleanup: + nxt_php_execute(ctx, r); nxt_free(ctx->script_filename.start); nxt_free(ctx->script_dirname.start); -} - -static int -nxt_php_startup(sapi_module_struct *sapi_module) -{ - return php_module_startup(sapi_module, &nxt_php_unit_module, 1); + nxt_php_last_target = -1; } -static nxt_int_t -nxt_php_request_init(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) { nxt_unit_field_t *f; + zend_file_handle file_handle; + + nxt_unit_req_debug(ctx->req, "PHP execute script %s", + ctx->script_filename.start); SG(server_context) = ctx; SG(options) |= SAPI_OPTION_NO_CHDIR; @@ -860,20 +891,6 @@ nxt_php_request_init(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) SG(request_info).path_translated = NULL; - nxt_unit_req_debug(ctx->req, "handle.filename = '%s'", - ctx->script_filename.start); - - if (nxt_php_script_filename.start != NULL) { - nxt_unit_req_debug(ctx->req, "run script %.*s in absolute mode", - (int) nxt_php_script_filename.length, - (char *) nxt_php_script_filename.start); - - } else { - nxt_unit_req_debug(ctx->req, "run script %.*s", - (int) ctx->script_filename.length, - (char *) ctx->script_filename.start); - } - #ifdef NXT_PHP7 if (nxt_slow_path(php_request_startup() == FAILURE)) { #else @@ -881,23 +898,45 @@ nxt_php_request_init(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) #endif nxt_unit_req_debug(ctx->req, "php_request_startup() failed"); - return NXT_ERROR; + nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); + return; } - return NXT_OK; + if (ctx->chdir) { + ctx->chdir = 0; + nxt_php_vcwd_chdir(ctx->req, ctx->script_dirname.start); + } + + nxt_memzero(&file_handle, sizeof(file_handle)); + + file_handle.type = ZEND_HANDLE_FILENAME; + file_handle.filename = (char *) ctx->script_filename.start; + + php_execute_script(&file_handle TSRMLS_CC); + + php_request_shutdown(NULL); + + nxt_unit_request_done(ctx->req, NXT_UNIT_OK); } nxt_inline void -nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, const nxt_str_t *dir) +nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir) { - if (nxt_slow_path(VCWD_CHDIR((char *) dir->start) != 0)) { + if (nxt_slow_path(VCWD_CHDIR((char *) dir) != 0)) { nxt_unit_req_alert(req, "VCWD_CHDIR(%s) failed (%d: %s)", - dir->start, errno, strerror(errno)); + dir, errno, strerror(errno)); } } +static int +nxt_php_startup(sapi_module_struct *sapi_module) +{ + return php_module_startup(sapi_module, &nxt_php_unit_module, 1); +} + + #ifdef NXT_PHP7 static size_t nxt_php_unbuffered_write(const char *str, size_t str_length TSRMLS_DC) @@ -1061,19 +1100,16 @@ nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) * available. */ - if (nxt_php_script_name.start != NULL) { - /* ABS_MODE */ - nxt_php_set_str(req, "PHP_SELF", &nxt_php_script_name, - track_vars_array TSRMLS_CC); - - } else { + if (ctx->path_info.length != 0) { nxt_php_set_sptr(req, "PHP_SELF", &r->path, r->path_length, track_vars_array TSRMLS_CC); - } - if (ctx->path_info.length != 0) { nxt_php_set_str(req, "PATH_INFO", &ctx->path_info, track_vars_array TSRMLS_CC); + + } else { + nxt_php_set_str(req, "PHP_SELF", &ctx->script_name, + track_vars_array TSRMLS_CC); } /* @@ -1100,7 +1136,7 @@ nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) * as defined in the server's configuration file. */ - nxt_php_set_str(req, "DOCUMENT_ROOT", &nxt_php_root, + nxt_php_set_str(req, "DOCUMENT_ROOT", ctx->root, track_vars_array TSRMLS_CC); nxt_php_set_sptr(req, "REQUEST_METHOD", &r->method, r->method_length, diff --git a/src/nxt_router.c b/src/nxt_router.c index a699effc..d94a34af 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -27,6 +27,7 @@ typedef struct { uint32_t requests; nxt_conf_value_t *limits_value; nxt_conf_value_t *processes_value; + nxt_conf_value_t *targets_value; } nxt_router_app_conf_t; @@ -1272,6 +1273,12 @@ static nxt_conf_map_t nxt_router_app_conf[] = { NXT_CONF_MAP_PTR, offsetof(nxt_router_app_conf_t, processes_value), }, + + { + nxt_string("targets"), + NXT_CONF_MAP_PTR, + offsetof(nxt_router_app_conf_t, targets_value), + }, }; @@ -1423,12 +1430,13 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, { u_char *p; size_t size; - nxt_mp_t *mp; - uint32_t next; + nxt_mp_t *mp, *app_mp; + uint32_t next, next_target; nxt_int_t ret; - nxt_str_t name, path; + nxt_str_t name, path, target; nxt_app_t *app, *prev; - nxt_str_t *t; + nxt_str_t *t, *s, *targets; + nxt_uint_t n, i; nxt_router_t *router; nxt_app_joint_t *app_joint; nxt_conf_value_t *conf, *http, *value, *websocket; @@ -1501,13 +1509,20 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, size = nxt_conf_json_length(application, NULL); - app = nxt_malloc(sizeof(nxt_app_t) + name.length + size); - if (app == NULL) { + app_mp = nxt_mp_create(4096, 128, 1024, 64); + if (nxt_slow_path(app_mp == NULL)) { goto fail; } + app = nxt_mp_get(app_mp, sizeof(nxt_app_t) + name.length + size); + if (app == NULL) { + goto app_fail; + } + nxt_memzero(app, sizeof(nxt_app_t)); + app->mem_pool = app_mp; + app->name.start = nxt_pointer_to(app, sizeof(nxt_app_t)); app->conf.start = nxt_pointer_to(app, sizeof(nxt_app_t) + name.length); @@ -1522,7 +1537,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, prev = nxt_router_app_find(&router->apps, &name); if (prev != NULL && nxt_strstr_eq(&app->conf, &prev->conf)) { - nxt_free(app); + nxt_mp_destroy(app_mp); nxt_queue_remove(&prev->link); nxt_queue_insert_tail(&tmcf->previous, &prev->link); @@ -1538,6 +1553,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, apcf.requests = 0; apcf.limits_value = NULL; apcf.processes_value = NULL; + apcf.targets_value = NULL; app_joint = nxt_malloc(sizeof(nxt_app_joint_t)); if (nxt_slow_path(app_joint == NULL)) { @@ -1587,6 +1603,30 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, apcf.spare_processes = apcf.processes; } + if (apcf.targets_value != NULL) { + n = nxt_conf_object_members_count(apcf.targets_value); + + targets = nxt_mp_get(app_mp, sizeof(nxt_str_t) * n); + if (nxt_slow_path(targets == NULL)) { + goto app_fail; + } + + next_target = 0; + + for (i = 0; i < n; i++) { + value = nxt_conf_next_object_member(apcf.targets_value, + &target, &next_target); + + s = nxt_str_dup(app_mp, &targets[i], &target); + if (nxt_slow_path(s == NULL)) { + goto app_fail; + } + } + + } else { + targets = NULL; + } + nxt_debug(task, "application type: %V", &apcf.type); nxt_debug(task, "application processes: %D", apcf.processes); nxt_debug(task, "application request timeout: %M", apcf.timeout); @@ -1628,6 +1668,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, app->max_pending_responses = 2; app->max_requests = apcf.requests; + app->targets = targets; + engine = task->thread->engine; app->engine = engine; @@ -1839,7 +1881,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, app_fail: - nxt_free(app); + nxt_mp_destroy(app_mp); fail: @@ -1847,7 +1889,7 @@ fail: nxt_queue_remove(&app->link); nxt_thread_mutex_destroy(&app->mutex); - nxt_free(app); + nxt_mp_destroy(app->mem_pool); } nxt_queue_loop; @@ -4538,7 +4580,7 @@ nxt_router_free_app(nxt_task_t *task, void *obj, void *data) nxt_assert(nxt_queue_is_empty(&app->idle_ports)); nxt_thread_mutex_destroy(&app->mutex); - nxt_free(app); + nxt_mp_destroy(app->mem_pool); app_joint->app = NULL; @@ -4992,6 +5034,8 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_http_request_t *r, req = (nxt_unit_request_t *) out->mem.free; out->mem.free += req_size; + req->app_target = r->app_target; + req->content_length = content_length; p = (u_char *) (req->fields + fields_count); diff --git a/src/nxt_router.h b/src/nxt_router.h index 08142ce3..6004a459 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -133,8 +133,11 @@ struct nxt_app_s { nxt_nsec_t res_timeout; nxt_msec_t idle_timeout; + nxt_str_t *targets; + nxt_app_type_t type:8; + nxt_mp_t *mem_pool; nxt_queue_link_t link; nxt_str_t conf; diff --git a/src/nxt_unit_request.h b/src/nxt_unit_request.h index 52017a42..fede00d2 100644 --- a/src/nxt_unit_request.h +++ b/src/nxt_unit_request.h @@ -21,6 +21,7 @@ struct nxt_unit_request_s { uint8_t local_length; uint8_t tls; uint8_t websocket_handshake; + uint8_t app_target; uint32_t server_name_length; uint32_t target_length; uint32_t path_length; |