From 113afb09ea7ddeebf2376cf6df3af212705e6128 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Thu, 22 Apr 2021 13:13:06 +0800 Subject: Router: grouped app and share fields in nxt_http_action_t. This is a prerequisite for further introduction of openat2() features. No functional changes. --- src/nxt_http.h | 12 +++++++++--- src/nxt_http_request.c | 4 ++-- src/nxt_http_route.c | 46 +++++++++++++++++++++++++++------------------- src/nxt_http_static.c | 12 ++++++------ src/nxt_router.c | 2 +- 5 files changed, 45 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/nxt_http.h b/src/nxt_http.h index e30bfeb4..2aa108ec 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -206,16 +206,22 @@ struct nxt_http_action_s { nxt_http_action_t *action); union { nxt_http_route_t *route; - nxt_app_t *application; - nxt_http_action_t *fallback; nxt_upstream_t *upstream; uint32_t upstream_number; nxt_http_status_t return_code; nxt_var_t *var; + + struct { + nxt_app_t *application; + nxt_int_t target; + } app; + + struct { + nxt_http_action_t *fallback; + } share; } u; nxt_str_t name; - nxt_int_t target; }; diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 650c1a89..779cfcf8 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -348,9 +348,9 @@ nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_str_set(&r->server_name, "localhost"); } - r->app_target = action->target; + r->app_target = action->u.app.target; - nxt_router_process_http_request(task, r, action->u.application); + nxt_router_process_http_request(task, r, action->u.app.application); return NULL; } diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 28545fc9..dfdb07df 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -627,14 +627,14 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = { offsetof(nxt_http_route_action_conf_t, location) }, { - nxt_string("share"), + nxt_string("proxy"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, share) + offsetof(nxt_http_route_action_conf_t, proxy) }, { - nxt_string("proxy"), + nxt_string("share"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, proxy) + offsetof(nxt_http_route_action_conf_t, share) }, { nxt_string("fallback"), @@ -700,14 +700,14 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, return NXT_OK; } - conf = accf.pass; - if (accf.share != NULL) { conf = accf.share; - action->handler = nxt_http_static_handler; } else if (accf.proxy != NULL) { conf = accf.proxy; + + } else { + conf = accf.pass; } nxt_conf_get_string(conf, &name); @@ -717,14 +717,21 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, return NXT_ERROR; } - if (accf.fallback != NULL) { - action->u.fallback = nxt_mp_alloc(mp, sizeof(nxt_http_action_t)); - if (nxt_slow_path(action->u.fallback == NULL)) { - return NXT_ERROR; + if (accf.share != NULL) { + action->handler = nxt_http_static_handler; + + if (accf.fallback != NULL) { + action->u.share.fallback = nxt_mp_alloc(mp, + sizeof(nxt_http_action_t)); + if (nxt_slow_path(action->u.share.fallback == NULL)) { + return NXT_ERROR; + } + + return nxt_http_route_action_create(tmcf, accf.fallback, + action->u.share.fallback); } - return nxt_http_route_action_create(tmcf, accf.fallback, - action->u.fallback); + return NXT_OK; } if (accf.proxy != NULL) { @@ -1411,9 +1418,10 @@ nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, if (action->handler != NULL) { if (action->handler == nxt_http_static_handler - && action->u.fallback != NULL) + && action->u.share.fallback != NULL) { - return nxt_http_action_resolve(task, tmcf, action->u.fallback); + return nxt_http_action_resolve(task, tmcf, + action->u.share.fallback); } return NXT_OK; @@ -1533,14 +1541,14 @@ nxt_http_pass_find(nxt_task_t *task, nxt_mp_t *mp, nxt_router_conf_t *rtcf, } if (segments[2].length != 0) { - targets = action->u.application->targets; + targets = action->u.app.application->targets; for (i = 0; !nxt_strstr_eq(&segments[2], &targets[i]); i++); - action->target = i; + action->u.app.target = i; } else { - action->target = 0; + action->u.app.target = 0; } return NXT_OK; @@ -1678,7 +1686,7 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf, (void) nxt_router_listener_application(rtcf, name, action); - action->target = 0; + action->u.app.target = 0; return action; } diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index df2655fc..c0b48586 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -49,8 +49,8 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, if (nxt_slow_path(!nxt_str_eq(r->method, "GET", 3))) { if (!nxt_str_eq(r->method, "HEAD", 4)) { - if (action->u.fallback != NULL) { - return action->u.fallback; + if (action->u.share.fallback != NULL) { + return action->u.share.fallback; } nxt_http_request_error(task, r, NXT_HTTP_METHOD_NOT_ALLOWED); @@ -127,8 +127,8 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, break; } - if (level == NXT_LOG_ERR && action->u.fallback != NULL) { - return action->u.fallback; + if (level == NXT_LOG_ERR && action->u.share.fallback != NULL) { + return action->u.share.fallback; } if (status != NXT_HTTP_NOT_FOUND) { @@ -230,8 +230,8 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_file_close(task, f); if (nxt_slow_path(!nxt_is_dir(&fi))) { - if (action->u.fallback != NULL) { - return action->u.fallback; + if (action->u.share.fallback != NULL) { + return action->u.share.fallback; } nxt_log(task, NXT_LOG_ERR, "\"%FN\" is not a regular file", diff --git a/src/nxt_router.c b/src/nxt_router.c index 8524b358..a20e4ede 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -2144,7 +2144,7 @@ nxt_router_listener_application(nxt_router_conf_t *rtcf, nxt_str_t *name, return NXT_DECLINED; } - action->u.application = app; + action->u.app.application = app; action->handler = nxt_http_application_handler; return NXT_OK; -- cgit From 53279af5d44dce2b679399d6a36eb46292928175 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Thu, 29 Apr 2021 22:04:34 +0800 Subject: Static: support for openat2() features. Support for chrooting, rejecting symlinks, and rejecting crossing mounting points on a per-request basis during static file serving. --- src/nxt_conf_validation.c | 32 ++++++++++++ src/nxt_errno.h | 1 + src/nxt_file.c | 44 +++++++++++++++++ src/nxt_file.h | 32 ++++++++++++ src/nxt_http.h | 2 + src/nxt_http_route.c | 63 +++++++++++++++++++++++- src/nxt_http_static.c | 123 ++++++++++++++++++++++++++++++++++++---------- src/nxt_unix.h | 4 ++ 8 files changed, 274 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 8c5d1ec7..ac1a81d8 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -75,6 +75,8 @@ static nxt_int_t nxt_conf_vldt_error(nxt_conf_validation_t *vldt, const char *fmt, ...); static nxt_int_t nxt_conf_vldt_var(nxt_conf_validation_t *vldt, const char *option, nxt_str_t *value); +nxt_inline nxt_int_t nxt_conf_vldt_unsupported(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_mtypes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); @@ -458,6 +460,27 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_share_action_members[] = { .name = nxt_string("fallback"), .type = NXT_CONF_VLDT_OBJECT, .validator = nxt_conf_vldt_action, + }, { + .name = nxt_string("chroot"), + .type = NXT_CONF_VLDT_STRING, +#if !(NXT_HAVE_OPENAT2) + .validator = nxt_conf_vldt_unsupported, + .u.string = "chroot", +#endif + }, { + .name = nxt_string("follow_symlinks"), + .type = NXT_CONF_VLDT_BOOLEAN, +#if !(NXT_HAVE_OPENAT2) + .validator = nxt_conf_vldt_unsupported, + .u.string = "follow_symlinks", +#endif + }, { + .name = nxt_string("traverse_mounts"), + .type = NXT_CONF_VLDT_BOOLEAN, +#if !(NXT_HAVE_OPENAT2) + .validator = nxt_conf_vldt_unsupported, + .u.string = "traverse_mounts", +#endif }, NXT_CONF_VLDT_END @@ -1032,6 +1055,15 @@ nxt_conf_vldt_error(nxt_conf_validation_t *vldt, const char *fmt, ...) } +nxt_inline nxt_int_t +nxt_conf_vldt_unsupported(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + return nxt_conf_vldt_error(vldt, "Unit is built without the \"%s\" " + "option support.", data); +} + + static nxt_int_t nxt_conf_vldt_var(nxt_conf_validation_t *vldt, const char *option, nxt_str_t *value) diff --git a/src/nxt_errno.h b/src/nxt_errno.h index 40bcfa3f..ec700537 100644 --- a/src/nxt_errno.h +++ b/src/nxt_errno.h @@ -22,6 +22,7 @@ typedef int nxt_err_t; #define NXT_EACCES EACCES #define NXT_EBUSY EBUSY #define NXT_EEXIST EEXIST +#define NXT_ELOOP ELOOP #define NXT_EXDEV EXDEV #define NXT_ENOTDIR ENOTDIR #define NXT_EISDIR EISDIR diff --git a/src/nxt_file.c b/src/nxt_file.c index a9595dd9..5d38d57e 100644 --- a/src/nxt_file.c +++ b/src/nxt_file.c @@ -42,6 +42,50 @@ nxt_file_open(nxt_task_t *task, nxt_file_t *file, nxt_uint_t mode, } +#if (NXT_HAVE_OPENAT2) + +nxt_int_t +nxt_file_openat2(nxt_task_t *task, nxt_file_t *file, nxt_uint_t mode, + nxt_uint_t create, nxt_file_access_t access, nxt_fd_t dfd, + nxt_uint_t resolve) +{ + struct open_how how; + + nxt_memzero(&how, sizeof(how)); + + /* O_NONBLOCK is to prevent blocking on FIFOs, special devices, etc. */ + mode |= (O_NONBLOCK | create); + + how.flags = mode; + how.mode = access; + how.resolve = resolve; + + file->fd = syscall(SYS_openat2, dfd, file->name, &how, sizeof(how)); + + file->error = (file->fd == -1) ? nxt_errno : 0; + +#if (NXT_DEBUG) + nxt_thread_time_update(task->thread); +#endif + + nxt_debug(task, "openat2(%FD, \"%FN\"): %FD err:%d", dfd, file->name, + file->fd, file->error); + + if (file->fd != -1) { + return NXT_OK; + } + + if (file->log_level != 0) { + nxt_log(task, file->log_level, "openat2(%FD, \"%FN\") failed %E", dfd, + file->name, file->error); + } + + return NXT_ERROR; +} + +#endif + + void nxt_file_close(nxt_task_t *task, nxt_file_t *file) { diff --git a/src/nxt_file.h b/src/nxt_file.h index 4f56e746..4846305b 100644 --- a/src/nxt_file.h +++ b/src/nxt_file.h @@ -109,6 +109,12 @@ typedef struct { NXT_EXPORT nxt_int_t nxt_file_open(nxt_task_t *task, nxt_file_t *file, nxt_uint_t mode, nxt_uint_t create, nxt_file_access_t access); +#if (NXT_HAVE_OPENAT2) +NXT_EXPORT nxt_int_t nxt_file_openat2(nxt_task_t *task, nxt_file_t *file, + nxt_uint_t mode, nxt_uint_t create, nxt_file_access_t access, nxt_fd_t dfd, + nxt_uint_t resolve); +#endif + /* The file open access modes. */ #define NXT_FILE_RDONLY O_RDONLY @@ -116,6 +122,32 @@ NXT_EXPORT nxt_int_t nxt_file_open(nxt_task_t *task, nxt_file_t *file, #define NXT_FILE_RDWR O_RDWR #define NXT_FILE_APPEND (O_WRONLY | O_APPEND) +#if (NXT_HAVE_OPENAT2) + +#if defined(O_DIRECTORY) +#define NXT_FILE_DIRECTORY O_DIRECTORY +#else +#define NXT_FILE_DIRECTORY 0 +#endif + +#if defined(O_SEARCH) +#define NXT_FILE_SEARCH (O_SEARCH|NXT_FILE_DIRECTORY) + +#elif defined(O_EXEC) +#define NXT_FILE_SEARCH (O_EXEC|NXT_FILE_DIRECTORY) + +#else +/* + * O_PATH is used in combination with O_RDONLY. The last one is ignored + * if O_PATH is used, but it allows Unit to not fail when it was built on + * modern system (i.e. glibc 2.14+) and run with a kernel older than 2.6.39. + * Then O_PATH is unknown to the kernel and ignored, while O_RDONLY is used. + */ +#define NXT_FILE_SEARCH (O_PATH|O_RDONLY|NXT_FILE_DIRECTORY) +#endif + +#endif /* NXT_HAVE_OPENAT2 */ + /* The file creation modes. */ #define NXT_FILE_CREATE_OR_OPEN O_CREAT #define NXT_FILE_OPEN 0 diff --git a/src/nxt_http.h b/src/nxt_http.h index 2aa108ec..da1124a8 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -217,6 +217,8 @@ struct nxt_http_action_s { } app; struct { + nxt_str_t chroot; + nxt_uint_t resolve; nxt_http_action_t *fallback; } share; } u; diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index dfdb07df..bfe5ce5f 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -50,8 +50,11 @@ typedef struct { nxt_conf_value_t *pass; nxt_conf_value_t *ret; nxt_str_t location; - nxt_conf_value_t *share; nxt_conf_value_t *proxy; + nxt_conf_value_t *share; + nxt_str_t chroot; + nxt_conf_value_t *follow_symlinks; + nxt_conf_value_t *traverse_mounts; nxt_conf_value_t *fallback; } nxt_http_route_action_conf_t; @@ -636,6 +639,21 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = { NXT_CONF_MAP_PTR, offsetof(nxt_http_route_action_conf_t, share) }, + { + nxt_string("chroot"), + NXT_CONF_MAP_STR, + offsetof(nxt_http_route_action_conf_t, chroot) + }, + { + nxt_string("follow_symlinks"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_action_conf_t, follow_symlinks) + }, + { + nxt_string("traverse_mounts"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_action_conf_t, traverse_mounts) + }, { nxt_string("fallback"), NXT_CONF_MAP_PTR, @@ -648,6 +666,11 @@ static nxt_int_t nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, nxt_http_action_t *action) { +#if (NXT_HAVE_OPENAT2) + u_char *p; + uint8_t slash; + nxt_str_t *chroot; +#endif nxt_mp_t *mp; nxt_int_t ret; nxt_str_t name, *string; @@ -720,6 +743,44 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, if (accf.share != NULL) { action->handler = nxt_http_static_handler; +#if (NXT_HAVE_OPENAT2) + string = &accf.chroot; + chroot = &action->u.share.chroot; + + if (string->length > 0) { + action->u.share.resolve |= RESOLVE_IN_ROOT; + + slash = (string->start[string->length - 1] != '/'); + + chroot->length = string->length + (slash ? 1 : 0); + + chroot->start = nxt_mp_alloc(mp, chroot->length + 1); + if (nxt_slow_path(chroot->start == NULL)) { + return NXT_ERROR; + } + + p = nxt_cpymem(chroot->start, string->start, string->length); + + if (slash) { + *p++ = '/'; + } + + *p = '\0'; + } + + if (accf.follow_symlinks != NULL + && !nxt_conf_get_boolean(accf.follow_symlinks)) + { + action->u.share.resolve |= RESOLVE_NO_SYMLINKS; + } + + if (accf.traverse_mounts != NULL + && !nxt_conf_get_boolean(accf.traverse_mounts)) + { + action->u.share.resolve |= RESOLVE_NO_XDEV; + } +#endif + if (accf.fallback != NULL) { action->u.share.fallback = nxt_mp_alloc(mp, sizeof(nxt_http_action_t)); diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index c0b48586..98d70739 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -31,15 +31,15 @@ nxt_http_action_t * nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action) { - size_t alloc, encode; - u_char *p; + size_t length, encode; + u_char *p, *fname; struct tm tm; nxt_buf_t *fb; nxt_int_t ret; - nxt_str_t index, extension, *mtype; + nxt_str_t index, extension, *mtype, *chroot; nxt_uint_t level; nxt_bool_t need_body; - nxt_file_t *f; + nxt_file_t *f, af, file; nxt_file_info_t fi; nxt_http_field_t *field; nxt_http_status_t status; @@ -63,13 +63,6 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, need_body = 1; } - f = nxt_mp_zget(r->mem_pool, sizeof(nxt_file_t)); - if (nxt_slow_path(f == NULL)) { - goto fail; - } - - f->fd = NXT_FILE_INVALID; - if (r->path->start[r->path->length - 1] == '/') { /* TODO: dynamic index setting. */ nxt_str_set(&index, "index.html"); @@ -80,23 +73,83 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_str_null(&extension); } - alloc = action->name.length + r->path->length + index.length + 1; + f = NULL; - f->name = nxt_mp_nget(r->mem_pool, alloc); - if (nxt_slow_path(f->name == NULL)) { + length = action->name.length + r->path->length + index.length; + + fname = nxt_mp_nget(r->mem_pool, length + 1); + if (nxt_slow_path(fname == NULL)) { goto fail; } - p = f->name; + p = fname; p = nxt_cpymem(p, action->name.start, action->name.length); p = nxt_cpymem(p, r->path->start, r->path->length); p = nxt_cpymem(p, index.start, index.length); *p = '\0'; - ret = nxt_file_open(task, f, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0); + nxt_memzero(&file, sizeof(nxt_file_t)); + + file.name = fname; + + chroot = &action->u.share.chroot; + +#if (NXT_HAVE_OPENAT2) + + if (action->u.share.resolve != 0) { + + if (chroot->length > 0) { + file.name = chroot->start; + + if (length > chroot->length + && nxt_memcmp(fname, chroot->start, chroot->length) == 0) + { + fname += chroot->length; + ret = nxt_file_open(task, &file, NXT_FILE_SEARCH, NXT_FILE_OPEN, + 0); + + } else { + file.error = NXT_EACCES; + ret = NXT_ERROR; + } + + } else if (fname[0] == '/') { + file.name = (u_char *) "/"; + ret = nxt_file_open(task, &file, NXT_FILE_SEARCH, NXT_FILE_OPEN, 0); + + } else { + file.name = (u_char *) "."; + file.fd = AT_FDCWD; + ret = NXT_OK; + } + + if (nxt_fast_path(ret == NXT_OK)) { + af = file; + nxt_memzero(&file, sizeof(nxt_file_t)); + file.name = fname; + + ret = nxt_file_openat2(task, &file, NXT_FILE_RDONLY, + NXT_FILE_OPEN, 0, af.fd, + action->u.share.resolve); + + if (af.fd != AT_FDCWD) { + nxt_file_close(task, &af); + } + } + + } else { + ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0); + } + +#else + + ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0); + +#endif if (nxt_slow_path(ret != NXT_OK)) { - switch (f->error) { + + switch (file.error) { /* * For Unix domain sockets "errno" is set to: @@ -117,6 +170,10 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, break; case NXT_EACCES: +#if (NXT_HAVE_OPENAT2) + case NXT_ELOOP: + case NXT_EXDEV: +#endif level = NXT_LOG_ERR; status = NXT_HTTP_FORBIDDEN; break; @@ -132,13 +189,27 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, } if (status != NXT_HTTP_NOT_FOUND) { - nxt_log(task, level, "open(\"%FN\") failed %E", f->name, f->error); + if (chroot->length > 0) { + nxt_log(task, level, "opening \"%FN\" at \"%FN\" failed %E", + fname, chroot, file.error); + + } else { + nxt_log(task, level, "opening \"%FN\" failed %E", + fname, file.error); + } } nxt_http_request_error(task, r, status); return NULL; } + f = nxt_mp_get(r->mem_pool, sizeof(nxt_file_t)); + if (nxt_slow_path(f == NULL)) { + goto fail; + } + + *f = file; + ret = nxt_file_info(f, &fi); if (nxt_slow_path(ret != NXT_OK)) { goto fail; @@ -172,15 +243,15 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_http_field_name_set(field, "ETag"); - alloc = NXT_TIME_T_HEXLEN + NXT_OFF_T_HEXLEN + 3; + length = NXT_TIME_T_HEXLEN + NXT_OFF_T_HEXLEN + 3; - p = nxt_mp_nget(r->mem_pool, alloc); + p = nxt_mp_nget(r->mem_pool, length); if (nxt_slow_path(p == NULL)) { goto fail; } field->value = p; - field->value_length = nxt_sprintf(p, p + alloc, "\"%xT-%xO\"", + field->value_length = nxt_sprintf(p, p + length, "\"%xT-%xO\"", nxt_file_mtime(&fi), nxt_file_size(&fi)) - p; @@ -254,19 +325,19 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_http_field_name_set(field, "Location"); encode = nxt_encode_uri(NULL, r->path->start, r->path->length); - alloc = r->path->length + encode * 2 + 1; + length = r->path->length + encode * 2 + 1; if (r->args->length > 0) { - alloc += 1 + r->args->length; + length += 1 + r->args->length; } - p = nxt_mp_nget(r->mem_pool, alloc); + p = nxt_mp_nget(r->mem_pool, length); if (nxt_slow_path(p == NULL)) { goto fail; } field->value = p; - field->value_length = alloc; + field->value_length = length; if (encode > 0) { p = (u_char *) nxt_encode_uri(p, r->path->start, r->path->length); @@ -294,7 +365,7 @@ fail: nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); - if (f != NULL && f->fd != NXT_FILE_INVALID) { + if (f != NULL) { nxt_file_close(task, f); } diff --git a/src/nxt_unix.h b/src/nxt_unix.h index 609f7e95..393f61d9 100644 --- a/src/nxt_unix.h +++ b/src/nxt_unix.h @@ -242,6 +242,10 @@ #include #endif +#if (NXT_HAVE_OPENAT2) +#include +#endif + #if (NXT_TEST_BUILD) #include #endif -- cgit From 8bea2977bc292a9631512734ff6c46b62b24cf26 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Wed, 5 May 2021 16:30:26 +0800 Subject: Fixed building without openat2(). --- src/nxt_http_static.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index 98d70739..fe3e19cc 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -39,7 +39,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_str_t index, extension, *mtype, *chroot; nxt_uint_t level; nxt_bool_t need_body; - nxt_file_t *f, af, file; + nxt_file_t *f, file; nxt_file_info_t fi; nxt_http_field_t *field; nxt_http_status_t status; @@ -124,6 +124,8 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, } if (nxt_fast_path(ret == NXT_OK)) { + nxt_file_t af; + af = file; nxt_memzero(&file, sizeof(nxt_file_t)); file.name = fname; -- cgit From de631d8c36cebdd69018dfa3ff145ba8b701a2d1 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Wed, 5 May 2021 17:23:33 +0800 Subject: Fixed format and arguments mismatches in error log messages. --- src/nxt_http_static.c | 4 ++-- src/nxt_pcre2.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index fe3e19cc..e603819b 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -192,11 +192,11 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, if (status != NXT_HTTP_NOT_FOUND) { if (chroot->length > 0) { - nxt_log(task, level, "opening \"%FN\" at \"%FN\" failed %E", + nxt_log(task, level, "opening \"%s\" at \"%V\" failed %E", fname, chroot, file.error); } else { - nxt_log(task, level, "opening \"%FN\" failed %E", + nxt_log(task, level, "opening \"%s\" failed %E", fname, file.error); } } diff --git a/src/nxt_pcre2.c b/src/nxt_pcre2.c index 22c4d2d4..cb51062c 100644 --- a/src/nxt_pcre2.c +++ b/src/nxt_pcre2.c @@ -144,7 +144,7 @@ nxt_regex_match(nxt_regex_t *re, u_char *subject, size_t length, if (pcre2_get_error_message(ret, errptr, ERR_BUF_SIZE) < 0) { nxt_thread_log_error(NXT_LOG_ERR, "pcre2_match() failed: %d on \"%*s\" " - "using \"%V\"", ret, subject, length, subject, + "using \"%V\"", ret, length, subject, &re->pattern); } else { -- cgit From b9d5eb285a2fca8b9b60d948acc679a5e16b3f94 Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Thu, 6 May 2021 14:22:21 +0000 Subject: Static: implemented MIME filtering --- src/nxt_conf_validation.c | 4 ++++ src/nxt_http.h | 6 +++++- src/nxt_http_route.c | 41 +++++++++++++++++++++++++++++------------ src/nxt_http_static.c | 39 +++++++++++++++++++++++++++++++++++---- 4 files changed, 73 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index ac1a81d8..1f654e53 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -456,6 +456,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_share_action_members[] = { { .name = nxt_string("share"), .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("types"), + .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, + .validator = nxt_conf_vldt_match_patterns, }, { .name = nxt_string("fallback"), .type = NXT_CONF_VLDT_OBJECT, diff --git a/src/nxt_http.h b/src/nxt_http.h index da1124a8..f82d837e 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -197,7 +197,8 @@ struct nxt_http_request_s { }; -typedef struct nxt_http_route_s nxt_http_route_t; +typedef struct nxt_http_route_s nxt_http_route_t; +typedef struct nxt_http_route_rule_s nxt_http_route_rule_t; struct nxt_http_action_s { @@ -219,6 +220,7 @@ struct nxt_http_action_s { struct { nxt_str_t chroot; nxt_uint_t resolve; + nxt_http_route_rule_t *types; nxt_http_action_t *fallback; } share; } u; @@ -306,6 +308,8 @@ nxt_int_t nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, nxt_str_t *segments, nxt_uint_t n); nxt_http_action_t *nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf, nxt_str_t *name); +nxt_int_t nxt_http_route_test_rule(nxt_http_request_t *r, + nxt_http_route_rule_t *rule, u_char *start, size_t length); nxt_int_t nxt_upstreams_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *conf); diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index bfe5ce5f..15b85544 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -55,6 +55,7 @@ typedef struct { nxt_str_t chroot; nxt_conf_value_t *follow_symlinks; nxt_conf_value_t *traverse_mounts; + nxt_conf_value_t *types; nxt_conf_value_t *fallback; } nxt_http_route_action_conf_t; @@ -115,7 +116,7 @@ typedef struct { } nxt_http_cookie_t; -typedef struct { +struct nxt_http_route_rule_s { /* The object must be the first field. */ nxt_http_route_object_t object:8; uint32_t items; @@ -131,7 +132,7 @@ typedef struct { } u; nxt_http_route_pattern_t pattern[0]; -} nxt_http_route_rule_t; +}; typedef struct { @@ -198,8 +199,9 @@ static nxt_http_route_t *nxt_http_route_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv); static nxt_http_route_match_t *nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv); -static nxt_int_t nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, - nxt_conf_value_t *cv, nxt_http_action_t *action); +static nxt_int_t nxt_http_route_action_create(nxt_task_t *task, + nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, + nxt_http_action_t *action); static nxt_http_route_table_t *nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *table_cv, nxt_http_route_object_t object, nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding); @@ -277,8 +279,6 @@ static nxt_http_name_value_t *nxt_http_route_cookie(nxt_array_t *array, u_char *name, size_t name_length, u_char *start, u_char *end); static nxt_int_t nxt_http_route_test_cookie(nxt_http_request_t *r, nxt_http_route_rule_t *rule, nxt_array_t *array); -static nxt_int_t nxt_http_route_test_rule(nxt_http_request_t *r, - nxt_http_route_rule_t *rule, u_char *start, size_t length); static nxt_int_t nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern, u_char *start, size_t length); static nxt_int_t nxt_http_route_memcmp(u_char *start, u_char *test, @@ -460,7 +460,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, match_conf = nxt_conf_get_path(cv, &match_path); n = (match_conf != NULL) ? nxt_conf_object_members_count(match_conf) : 0; - size = sizeof(nxt_http_route_match_t) + n * sizeof(nxt_http_route_rule_t *); + size = sizeof(nxt_http_route_match_t) + n * sizeof(nxt_http_route_test_t *); mp = tmcf->router_conf->mem_pool; @@ -476,7 +476,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NULL; } - ret = nxt_http_route_action_create(tmcf, action_conf, &match->action); + ret = nxt_http_route_action_create(task, tmcf, action_conf, &match->action); if (nxt_slow_path(ret != NXT_OK)) { return NULL; } @@ -654,6 +654,11 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = { NXT_CONF_MAP_PTR, offsetof(nxt_http_route_action_conf_t, traverse_mounts) }, + { + nxt_string("types"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_action_conf_t, types) + }, { nxt_string("fallback"), NXT_CONF_MAP_PTR, @@ -663,8 +668,8 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = { static nxt_int_t -nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, - nxt_http_action_t *action) +nxt_http_route_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_conf_value_t *cv, nxt_http_action_t *action) { #if (NXT_HAVE_OPENAT2) u_char *p; @@ -676,6 +681,7 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, nxt_str_t name, *string; nxt_uint_t encode; nxt_conf_value_t *conf; + nxt_http_route_rule_t *rule; nxt_http_route_action_conf_t accf; nxt_memzero(&accf, sizeof(accf)); @@ -781,6 +787,17 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, } #endif + if (accf.types != NULL) { + rule = nxt_http_route_rule_create(task, mp, accf.types, 0, + NXT_HTTP_ROUTE_PATTERN_LOWCASE, + NXT_HTTP_ROUTE_ENCODING_NONE); + if (nxt_slow_path(rule == NULL)) { + return NXT_ERROR; + } + + action->u.share.types = rule; + } + if (accf.fallback != NULL) { action->u.share.fallback = nxt_mp_alloc(mp, sizeof(nxt_http_action_t)); @@ -788,7 +805,7 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, return NXT_ERROR; } - return nxt_http_route_action_create(tmcf, accf.fallback, + return nxt_http_route_action_create(task, tmcf, accf.fallback, action->u.share.fallback); } @@ -2495,7 +2512,7 @@ nxt_http_route_test_cookie(nxt_http_request_t *r, } -static nxt_int_t +nxt_int_t nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule, u_char *start, size_t length) { diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index e603819b..c961bb97 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -75,6 +75,37 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, f = NULL; + rtcf = r->conf->socket_conf->router_conf; + + mtype = NULL; + + if (action->u.share.types != NULL && extension.start == NULL) { + nxt_http_static_extract_extension(r->path, &extension); + mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash, + &extension); + + if (mtype != NULL) { + ret = nxt_http_route_test_rule(r, action->u.share.types, + mtype->start, mtype->length); + if (ret == 1) { + goto mime_ok; + } + + if (nxt_slow_path(ret == NXT_ERROR)) { + goto fail; + } + } + + if (action->u.share.fallback != NULL) { + return action->u.share.fallback; + } + + nxt_http_request_error(task, r, NXT_HTTP_FORBIDDEN); + return NULL; + } + +mime_ok: + length = action->name.length + r->path->length + index.length; fname = nxt_mp_nget(r->mem_pool, length + 1); @@ -262,10 +293,10 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_http_static_extract_extension(r->path, &extension); } - rtcf = r->conf->socket_conf->router_conf; - - mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash, - &extension); + if (mtype == NULL) { + mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash, + &extension); + } if (mtype != NULL) { field = nxt_list_zero_add(r->resp.fields); -- cgit From b0e32bc015f8eb146e745b7184549e15c6657f85 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Fri, 7 May 2021 07:46:25 +0300 Subject: PHP: forced initialization of $_SERVER in fastcgi_finish_request(). The "auto_globals_jit" PHP option postponed the initialization of the $_SERVER global variable until the script using it had been loaded (e. g. via the "include" expression). As a result, nxt_php_register_variables() could be called after fastcgi_finish_request() had finished the request and nulled ctx->req, which thus caused a segmentation fault. --- src/nxt_php_sapi.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index 8fbe7f65..23c148c8 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -163,7 +163,8 @@ static const zend_function_entry nxt_php_ext_functions[] = { }; -zif_handler nxt_php_chdir_handler; +zif_handler nxt_php_chdir_handler; +zend_auto_global *nxt_php_server_ag; static zend_module_entry nxt_php_unit_module = { @@ -211,6 +212,7 @@ ZEND_NAMED_FUNCTION(nxt_php_chdir) PHP_FUNCTION(fastcgi_finish_request) { + zend_auto_global *ag; nxt_php_run_ctx_t *ctx; if (nxt_slow_path(zend_parse_parameters_none() == FAILURE)) { @@ -240,6 +242,16 @@ PHP_FUNCTION(fastcgi_finish_request) php_header(TSRMLS_C); #endif + ag = nxt_php_server_ag; + + if (ag->armed) { +#ifdef NXT_PHP7 + ag->armed = ag->auto_global_callback(ag->name); +#else + ag->armed = ag->auto_global_callback(ag->name, ag->name_len TSRMLS_CC); +#endif + } + nxt_unit_request_done(ctx->req, NXT_UNIT_OK); ctx->req = NULL; @@ -411,6 +423,19 @@ nxt_php_setup(nxt_task_t *task, nxt_process_t *process, nxt_php_set_options(task, value, ZEND_INI_USER); } +#ifdef NXT_PHP7 + nxt_php_server_ag = zend_hash_str_find_ptr(CG(auto_globals), "_SERVER", + nxt_length("_SERVER")); +#else + zend_hash_quick_find(CG(auto_globals), "_SERVER", sizeof("_SERVER"), + zend_hash_func("_SERVER", sizeof("_SERVER")), + (void **) &nxt_php_server_ag); +#endif + if (nxt_slow_path(nxt_php_server_ag == NULL)) { + nxt_alert(task, "failed to find $_SERVER auto global"); + return NXT_ERROR; + } + return NXT_OK; } -- cgit From a0c083af208cd9f676bb56762b4e27a3174a773d Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Wed, 12 May 2021 09:26:55 +0000 Subject: Node.js: a shim for overriding "http" and "websocket" modules. Also added stubs for Server.address() This was done to prevent crashes in some popular frameworks like express Supports both CommonJS and the new ES Modules system syntax e.g: app.js: const http = require('http') app.mjs: import http from "http" Usage on Node 14.16.x and higher: { "type": "external", "processes": {"spare": 0}, "working_directory": '/project', "executable": "/usr/bin/env", "arguments": [ "node", "--loader", "unit-http/require_shim.mjs" "--require", "unit-http/require_shim", "app.js" ] } Usage on Node 14.15.x and lower: { "type": "external", "processes": {"spare": 0}, "working_directory": '/project', "executable": "/usr/bin/env", "arguments": [ "node", "--require", "unit-http/require_shim", "app.js" ] } --- src/nodejs/unit-http/http.js | 15 +++++++++------ src/nodejs/unit-http/http_server.js | 24 ++++++++++++++++++------ src/nodejs/unit-http/require_shim.js | 27 +++++++++++++++++++++++++++ src/nodejs/unit-http/require_shim.mjs | 18 ++++++++++++++++++ 4 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 src/nodejs/unit-http/require_shim.js create mode 100644 src/nodejs/unit-http/require_shim.mjs (limited to 'src') diff --git a/src/nodejs/unit-http/http.js b/src/nodejs/unit-http/http.js index 3a25fa2f..d298a35f 100644 --- a/src/nodejs/unit-http/http.js +++ b/src/nodejs/unit-http/http.js @@ -5,19 +5,22 @@ 'use strict'; -const server = require('unit-http/http_server'); - -const { Server } = server; +const { + Server, + ServerRequest, + ServerResponse, +} = require('./http_server'); function createServer (requestHandler) { return new Server(requestHandler); } +const http = require("http") module.exports = { + ...http, Server, - STATUS_CODES: server.STATUS_CODES, createServer, - IncomingMessage: server.ServerRequest, - ServerResponse: server.ServerResponse + IncomingMessage: ServerRequest, + ServerResponse, }; diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index e59296ae..89964ec3 100644 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -444,17 +444,30 @@ Server.prototype.setTimeout = function setTimeout(msecs, callback) { Server.prototype.listen = function (...args) { this.unit.listen(); - const cb = args.pop(); - - if (typeof cb === 'function') { - this.once('listening', cb); + if (typeof args[args.length - 1] === 'function') { + this.once('listening', args[args.length - 1]); } - this.emit('listening'); + /* + * Some express.js apps use the returned server object inside the listening + * callback, so we timeout the listening event to occur after this function + * returns. + */ + setImmediate(function() { + this.emit('listening') + }.bind(this)) return this; }; +Server.prototype.address = function () { + return { + family: "IPv4", + address: "127.0.0.1", + port: 80 + } +} + Server.prototype.emit_request = function (req, res) { if (req._websocket_handshake && this._upgradeListenerCount > 0) { this.emit('upgrade', req, req.socket); @@ -530,7 +543,6 @@ function connectionListener(socket) { } module.exports = { - STATUS_CODES: http.STATUS_CODES, Server, ServerResponse, ServerRequest, diff --git a/src/nodejs/unit-http/require_shim.js b/src/nodejs/unit-http/require_shim.js new file mode 100644 index 00000000..2b307629 --- /dev/null +++ b/src/nodejs/unit-http/require_shim.js @@ -0,0 +1,27 @@ +// can only be ran as part of a --require param on the node process +if (module.parent && module.parent.id === "internal/preload") { + const { Module } = require("module") + + if (!Module.prototype.require.__unit_shim) { + const http = require("./http") + const websocket = require("./websocket") + + const original = Module.prototype.require; + + Module.prototype.require = function (id) { + switch(id) { + case "http": + case "unit-http": + return http + + case "websocket": + case "unit-http/websocket": + return websocket + } + + return original.apply(this, arguments); + } + + Module.prototype.require.__unit_shim = true; + } +} diff --git a/src/nodejs/unit-http/require_shim.mjs b/src/nodejs/unit-http/require_shim.mjs new file mode 100644 index 00000000..067d63d4 --- /dev/null +++ b/src/nodejs/unit-http/require_shim.mjs @@ -0,0 +1,18 @@ +// must be ran as part of a --loader or --experimental-loader param +export async function resolve(specifier, context, defaultResolver) { + switch (specifier) { + case "websocket": + return { + url: new URL("./websocket.js", import.meta.url).href, + format: "cjs" + } + + case "http": + return { + url: new URL("./http.js", import.meta.url).href, + format: "cjs" + } + } + + return defaultResolver(specifier, context, defaultResolver) +} -- cgit From c216f26d3040beef46ca25f73e580153c4c59d02 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Mon, 17 May 2021 17:34:15 +0300 Subject: Fixing racing condition on listen socket close in router. Listen socket is actually closed in the instant timer handler. This patch moves the "configuration has been applied" notification to the timer handler to avoid a situation when the user gets the response from the controller, but the listen socket is still open in the router. --- src/nxt_router.c | 24 ++++++++++++++---------- src/nxt_router.h | 2 ++ 2 files changed, 16 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nxt_router.c b/src/nxt_router.c index a20e4ede..da38aac0 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -3223,12 +3223,11 @@ nxt_router_listen_socket_update(nxt_task_t *task, void *obj, void *data) static void nxt_router_listen_socket_delete(nxt_task_t *task, void *obj, void *data) { - nxt_joint_job_t *job; - nxt_socket_conf_t *skcf; - nxt_listen_event_t *lev; - nxt_event_engine_t *engine; + nxt_socket_conf_t *skcf; + nxt_listen_event_t *lev; + nxt_event_engine_t *engine; + nxt_socket_conf_joint_t *joint; - job = obj; skcf = data; engine = task->thread->engine; @@ -3240,15 +3239,13 @@ nxt_router_listen_socket_delete(nxt_task_t *task, void *obj, void *data) nxt_debug(task, "engine %p: listen socket delete: %d", engine, lev->socket.fd); + joint = lev->socket.data; + joint->close_job = obj; + lev->timer.handler = nxt_router_listen_socket_close; lev->timer.work_queue = &engine->fast_work_queue; nxt_timer_add(engine, &lev->timer, 0); - - job->work.next = NULL; - job->work.handler = nxt_router_conf_wait; - - nxt_event_engine_post(job->tmcf->engine, &job->work); } @@ -3273,6 +3270,7 @@ static void nxt_router_listen_socket_close(nxt_task_t *task, void *obj, void *data) { nxt_timer_t *timer; + nxt_joint_job_t *job; nxt_listen_event_t *lev; nxt_socket_conf_joint_t *joint; @@ -3287,6 +3285,12 @@ nxt_router_listen_socket_close(nxt_task_t *task, void *obj, void *data) joint = lev->socket.data; lev->socket.data = NULL; + job = joint->close_job; + job->work.next = NULL; + job->work.handler = nxt_router_conf_wait; + + nxt_event_engine_post(job->tmcf->engine, &job->work); + /* 'task' refers to lev->task and we cannot use after nxt_free() */ task = &task->thread->engine->task; diff --git a/src/nxt_router.h b/src/nxt_router.h index 5804840f..b1ccdf51 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -205,6 +205,8 @@ typedef struct { nxt_event_engine_t *engine; nxt_socket_conf_t *socket_conf; + nxt_joint_job_t *close_job; + nxt_upstream_t **upstreams; /* Modules configuraitons. */ -- cgit From 19dfeba86b9dda6f1960ba9b3dba4708565d27ad Mon Sep 17 00:00:00 2001 From: Andrey Suvorov Date: Mon, 17 May 2021 14:28:38 -0700 Subject: Fixing a crash after applying the wrong TLS configuration. When an invalid TLS configuration is applied (such as the conf_commands feature), nxt_cert_store_get() creates a buffer to send a certificate request to the main process and adds its default completion handler to an asynchronous queue to free the allocated buffer. However, if configuration fails, nxt_router_conf_error() removes the memory pool used to allocate the buffer, causing a crash when the completion handler is dispatched. Assertion "src/nxt_buf.c:208 assertion failed: data == b->parent" is triggered when is NXT_DEBUG enabled in the configure script. This patch uses a reference counter to retain the memory pool and redefines the completion handler to free the buffer before releasing the memory pool. --- src/nxt_cert.c | 19 +++++++++++++++++++ src/nxt_router.c | 6 +++--- 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nxt_cert.c b/src/nxt_cert.c index 3cdb69c1..1806bc19 100644 --- a/src/nxt_cert.c +++ b/src/nxt_cert.c @@ -48,6 +48,7 @@ static nxt_conf_value_t *nxt_cert_name_details(nxt_mp_t *mp, X509 *x509, nxt_bool_t issuer); static nxt_conf_value_t *nxt_cert_alt_names_details(nxt_mp_t *mp, STACK_OF(GENERAL_NAME) *alt_names); +static void nxt_cert_buf_completion(nxt_task_t *task, void *obj, void *data); static nxt_lvlhsh_t nxt_cert_info; @@ -1073,6 +1074,9 @@ nxt_cert_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp, goto fail; } + nxt_mp_retain(mp); + b->completion_handler = nxt_cert_buf_completion; + nxt_buf_cpystr(b, name); *b->mem.free++ = '\0'; @@ -1102,6 +1106,21 @@ fail: } +static void +nxt_cert_buf_completion(nxt_task_t *task, void *obj, void *data) +{ + nxt_mp_t *mp; + nxt_buf_t *b; + + b = obj; + mp = b->data; + nxt_assert(b->next == NULL); + + nxt_mp_free(mp, b); + nxt_mp_release(mp); +} + + void nxt_cert_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) { diff --git a/src/nxt_router.c b/src/nxt_router.c index da38aac0..2bbe87b8 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -773,7 +773,7 @@ fail: msg->port_msg.stream, 0, NULL); if (tmcf != NULL) { - nxt_mp_destroy(tmcf->mem_pool); + nxt_mp_release(tmcf->mem_pool); } cleanup: @@ -1061,7 +1061,7 @@ nxt_router_conf_ready(nxt_task_t *task, nxt_router_temp_conf_t *tmcf) nxt_mp_destroy(rtcf->mem_pool); } - nxt_mp_destroy(tmcf->mem_pool); + nxt_mp_release(tmcf->mem_pool); } @@ -1120,7 +1120,7 @@ nxt_router_conf_error(nxt_task_t *task, nxt_router_temp_conf_t *tmcf) nxt_router_conf_send(task, tmcf, NXT_PORT_MSG_RPC_ERROR); - nxt_mp_destroy(tmcf->mem_pool); + nxt_mp_release(tmcf->mem_pool); } -- cgit From ead6ed999a87fac6fc9ad3f4f94a6cec1d287b5e Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Tue, 18 May 2021 10:14:43 +0000 Subject: Ruby: changing deprecated rb_cData to rb_cObject. Ruby 3.0 deprecated rb_cData with the intention to remove it in release 3.1. This commit changes references of rb_cData to rb_cObject. This was done so we can support distributions that package Ruby 3.0, such as Fedora 34. We also need to call rb_undef_alloc_func because we're no longer deriving from rb_cData. This prevents unnecessary allocations. See: https://docs.ruby-lang.org/en/3.0.0/doc/extension_rdoc.html "It is recommended that klass derives from a special class called Data (rb_cData) but not from Object or other ordinal classes. If it doesn't, you have to call rb_undef_alloc_func(klass)." --- src/ruby/nxt_ruby_stream_io.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ruby/nxt_ruby_stream_io.c b/src/ruby/nxt_ruby_stream_io.c index 69bf289e..82ad3908 100644 --- a/src/ruby/nxt_ruby_stream_io.c +++ b/src/ruby/nxt_ruby_stream_io.c @@ -25,7 +25,9 @@ nxt_ruby_stream_io_input_init(void) { VALUE stream_io; - stream_io = rb_define_class("NGINX_Unit_Stream_IO_Read", rb_cData); + stream_io = rb_define_class("NGINX_Unit_Stream_IO_Read", rb_cObject); + + rb_undef_alloc_func(stream_io); rb_gc_register_address(&stream_io); @@ -46,7 +48,9 @@ nxt_ruby_stream_io_error_init(void) { VALUE stream_io; - stream_io = rb_define_class("NGINX_Unit_Stream_IO_Error", rb_cData); + stream_io = rb_define_class("NGINX_Unit_Stream_IO_Error", rb_cObject); + + rb_undef_alloc_func(stream_io); rb_gc_register_address(&stream_io); -- cgit From f60389a782470e31dc555ab864784b536f2544ca Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Thu, 20 May 2021 13:02:45 +0000 Subject: Python: support for multiple targets. --- src/nxt_application.h | 1 + src/nxt_conf_validation.c | 132 +++++++++++++++++++++++++-- src/nxt_main_process.c | 6 ++ src/python/nxt_python.c | 164 +++++++++++++++++++++++++++------- src/python/nxt_python.h | 17 +++- src/python/nxt_python_asgi.c | 37 ++++---- src/python/nxt_python_asgi.h | 4 +- src/python/nxt_python_asgi_lifespan.c | 93 +++++++++++++++---- src/python/nxt_python_wsgi.c | 5 +- 9 files changed, 380 insertions(+), 79 deletions(-) (limited to 'src') diff --git a/src/nxt_application.h b/src/nxt_application.h index 632c5632..45e7fa48 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -54,6 +54,7 @@ typedef struct { nxt_str_t protocol; uint32_t threads; uint32_t thread_stack_size; + nxt_conf_value_t *targets; } nxt_python_app_conf_t; diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 1f654e53..f8381cde 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -100,6 +100,14 @@ static nxt_int_t nxt_conf_vldt_return(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_python(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_python_targets_exclusive( + nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_python_targets(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_python_target(nxt_conf_validation_t *vldt, + nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_python_path_element(nxt_conf_validation_t *vldt, @@ -518,7 +526,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_external_members[] = { }; -static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { +static nxt_conf_vldt_object_t nxt_conf_vldt_python_common_members[] = { { .name = nxt_string("home"), .type = NXT_CONF_VLDT_STRING, @@ -526,13 +534,6 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { .name = nxt_string("path"), .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, .validator = nxt_conf_vldt_python_path, - }, { - .name = nxt_string("module"), - .type = NXT_CONF_VLDT_STRING, - .flags = NXT_CONF_VLDT_REQUIRED, - }, { - .name = nxt_string("callable"), - .type = NXT_CONF_VLDT_STRING, }, { .name = nxt_string("protocol"), .type = NXT_CONF_VLDT_STRING, @@ -550,6 +551,54 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) }; +static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { + { + .name = nxt_string("module"), + .type = NXT_CONF_VLDT_STRING, + .validator = nxt_conf_vldt_python_targets_exclusive, + .u.string = "module", + }, { + .name = nxt_string("callable"), + .type = NXT_CONF_VLDT_STRING, + .validator = nxt_conf_vldt_python_targets_exclusive, + .u.string = "callable", + }, { + .name = nxt_string("targets"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = nxt_conf_vldt_python_targets, + }, + + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_python_common_members) +}; + + +static nxt_conf_vldt_object_t nxt_conf_vldt_python_target_members[] = { + { + .name = nxt_string("module"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + }, { + .name = nxt_string("callable"), + .type = NXT_CONF_VLDT_STRING, + }, + + NXT_CONF_VLDT_END +}; + + +static nxt_conf_vldt_object_t nxt_conf_vldt_python_notargets_members[] = { + { + .name = nxt_string("module"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + }, { + .name = nxt_string("callable"), + .type = NXT_CONF_VLDT_STRING, + }, + + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_python_common_members) +}; + static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { { @@ -1419,6 +1468,71 @@ nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, } +static nxt_int_t +nxt_conf_vldt_python(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + nxt_conf_value_t *targets; + + static nxt_str_t targets_str = nxt_string("targets"); + + targets = nxt_conf_get_object_member(value, &targets_str, NULL); + + if (targets != NULL) { + return nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_python_members); + } + + return nxt_conf_vldt_object(vldt, value, + nxt_conf_vldt_python_notargets_members); +} + + +static nxt_int_t +nxt_conf_vldt_python_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_python_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_python_target); +} + + +static nxt_int_t +nxt_conf_vldt_python_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 Python target name must not be empty."); + } + + if (nxt_conf_type(value) != NXT_CONF_OBJECT) { + return nxt_conf_vldt_error(vldt, "The \"%V\" Python target must be " + "an object.", name); + } + + return nxt_conf_vldt_object(vldt, value, + &nxt_conf_vldt_python_target_members); +} + + static nxt_int_t nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) @@ -1959,7 +2073,7 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name, } types[] = { { nxt_conf_vldt_object, nxt_conf_vldt_external_members }, - { nxt_conf_vldt_object, nxt_conf_vldt_python_members }, + { nxt_conf_vldt_python, NULL }, { nxt_conf_vldt_php, NULL }, { nxt_conf_vldt_object, nxt_conf_vldt_perl_members }, { nxt_conf_vldt_object, nxt_conf_vldt_ruby_members }, diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index f20f2c2c..00f336f6 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -210,6 +210,12 @@ static nxt_conf_map_t nxt_python_app_conf[] = { offsetof(nxt_common_app_conf_t, u.python.threads), }, + { + nxt_string("targets"), + NXT_CONF_MAP_PTR, + offsetof(nxt_common_app_conf_t, u.python.targets), + }, + { nxt_string("thread_stack_size"), NXT_CONF_MAP_INT32, diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c index d8204937..588a147a 100644 --- a/src/python/nxt_python.c +++ b/src/python/nxt_python.c @@ -24,6 +24,8 @@ typedef struct { static nxt_int_t nxt_python_start(nxt_task_t *task, nxt_process_data_t *data); +static nxt_int_t nxt_python_set_target(nxt_task_t *task, + nxt_python_target_t *target, nxt_conf_value_t *conf); static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value); static int nxt_python_init_threads(nxt_python_app_conf_t *c); static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx); @@ -49,7 +51,7 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { }; static PyObject *nxt_py_stderr_flush; -PyObject *nxt_py_application; +nxt_python_targets_t *nxt_py_targets; #if PY_MAJOR_VERSION == 3 static wchar_t *nxt_py_home; @@ -66,18 +68,19 @@ static nxt_int_t nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) { int rc; - char *nxt_py_module; - size_t len; + size_t len, size; + uint32_t next; PyObject *obj, *module; - nxt_str_t proto; - const char *callable; + nxt_str_t proto, probe_proto, name; + nxt_int_t ret, n, i; nxt_unit_ctx_t *unit_ctx; nxt_unit_init_t python_init; + nxt_conf_value_t *cv; + nxt_python_targets_t *targets; nxt_common_app_conf_t *app_conf; nxt_python_app_conf_t *c; #if PY_MAJOR_VERSION == 3 char *path; - size_t size; nxt_int_t pep405; static const char pyvenv[] = "/pyvenv.cfg"; @@ -190,38 +193,42 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) Py_CLEAR(obj); - nxt_py_module = nxt_alloca(c->module.length + 1); - nxt_memcpy(nxt_py_module, c->module.start, c->module.length); - nxt_py_module[c->module.length] = '\0'; + n = (c->targets != NULL ? nxt_conf_object_members_count(c->targets) : 1); - module = PyImport_ImportModule(nxt_py_module); - if (nxt_slow_path(module == NULL)) { - nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module); - nxt_python_print_exception(); + size = sizeof(nxt_python_targets_t) + n * sizeof(nxt_python_target_t); + + targets = nxt_unit_malloc(NULL, size); + if (nxt_slow_path(targets == NULL)) { + nxt_alert(task, "Could not allocate targets"); goto fail; } - callable = (c->callable != NULL) ? c->callable : "application"; + memset(targets, 0, size); - obj = PyDict_GetItemString(PyModule_GetDict(module), callable); - if (nxt_slow_path(obj == NULL)) { - nxt_alert(task, "Python failed to get \"%s\" " - "from module \"%s\"", callable, nxt_py_module); - goto fail; - } + targets->count = n; + nxt_py_targets = targets; - if (nxt_slow_path(PyCallable_Check(obj) == 0)) { - nxt_alert(task, "\"%s\" in module \"%s\" " - "is not a callable object", callable, nxt_py_module); - goto fail; - } + if (c->targets != NULL) { + next = 0; - nxt_py_application = obj; - obj = NULL; + for (i = 0; /* void */; i++) { + cv = nxt_conf_next_object_member(c->targets, &name, &next); + if (cv == NULL) { + break; + } - Py_INCREF(nxt_py_application); + ret = nxt_python_set_target(task, &targets->target[i], cv); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + } - Py_CLEAR(module); + } else { + ret = nxt_python_set_target(task, &targets->target[0], app_conf->self); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + } nxt_unit_default_init(task, &python_init); @@ -232,7 +239,18 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) proto = c->protocol; if (proto.length == 0) { - proto = nxt_python_asgi_check(nxt_py_application) ? asgi : wsgi; + proto = nxt_python_asgi_check(targets->target[0].application) + ? asgi : wsgi; + + for (i = 1; i < targets->count; i++) { + probe_proto = nxt_python_asgi_check(targets->target[i].application) + ? asgi : wsgi; + if (probe_proto.start != proto.start) { + nxt_alert(task, "A mix of ASGI & WSGI targets is forbidden, " + "specify protocol in config if incorrect"); + goto fail; + } + } } if (nxt_strstr_eq(&proto, &asgi)) { @@ -298,6 +316,81 @@ fail: } +static nxt_int_t +nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target, + nxt_conf_value_t *conf) +{ + char *callable, *module_name; + PyObject *module, *obj; + nxt_str_t str; + nxt_conf_value_t *value; + + static nxt_str_t module_str = nxt_string("module"); + static nxt_str_t callable_str = nxt_string("callable"); + + module = obj = NULL; + + value = nxt_conf_get_object_member(conf, &module_str, NULL); + if (nxt_slow_path(value == NULL)) { + goto fail; + } + + nxt_conf_get_string(value, &str); + + module_name = nxt_alloca(str.length + 1); + nxt_memcpy(module_name, str.start, str.length); + module_name[str.length] = '\0'; + + module = PyImport_ImportModule(module_name); + if (nxt_slow_path(module == NULL)) { + nxt_alert(task, "Python failed to import module \"%s\"", module_name); + nxt_python_print_exception(); + goto fail; + } + + value = nxt_conf_get_object_member(conf, &callable_str, NULL); + if (value == NULL) { + callable = nxt_alloca(12); + nxt_memcpy(callable, "application", 12); + + } else { + nxt_conf_get_string(value, &str); + + callable = nxt_alloca(str.length + 1); + nxt_memcpy(callable, str.start, str.length); + callable[str.length] = '\0'; + } + + obj = PyDict_GetItemString(PyModule_GetDict(module), callable); + if (nxt_slow_path(obj == NULL)) { + nxt_alert(task, "Python failed to get \"%s\" from module \"%s\"", + callable, module); + goto fail; + } + + if (nxt_slow_path(PyCallable_Check(obj) == 0)) { + nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object", + callable, module); + goto fail; + } + + target->application = obj; + obj = NULL; + + Py_INCREF(target->application); + Py_CLEAR(module); + + return NXT_OK; + +fail: + + Py_XDECREF(obj); + Py_XDECREF(module); + + return NXT_ERROR; +} + + static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value) { @@ -596,12 +689,21 @@ nxt_python_done_strings(nxt_python_string_t *pstr) static void nxt_python_atexit(void) { + nxt_int_t i; + if (nxt_py_proto.done != NULL) { nxt_py_proto.done(); } Py_XDECREF(nxt_py_stderr_flush); - Py_XDECREF(nxt_py_application); + + if (nxt_py_targets != NULL) { + for (i = 0; i < nxt_py_targets->count; i++) { + Py_XDECREF(nxt_py_targets->target[i].application); + } + + nxt_unit_free(NULL, nxt_py_targets); + } Py_Finalize(); diff --git a/src/python/nxt_python.h b/src/python/nxt_python.h index b581dd46..a5c1d9a6 100644 --- a/src/python/nxt_python.h +++ b/src/python/nxt_python.h @@ -37,13 +37,28 @@ #define NXT_HAVE_ASGI 1 #endif -extern PyObject *nxt_py_application; + +typedef struct { + PyObject *application; + nxt_bool_t asgi_legacy; +} nxt_python_target_t; + + +typedef struct { + nxt_int_t count; + nxt_python_target_t target[0]; +} nxt_python_targets_t; + + +extern nxt_python_targets_t *nxt_py_targets; + typedef struct { nxt_str_t string; PyObject **object_p; } nxt_python_string_t; + typedef struct { int (*ctx_data_alloc)(void **pdata); void (*ctx_data_free)(void *data); diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c index a6f94507..1d220678 100644 --- a/src/python/nxt_python_asgi.c +++ b/src/python/nxt_python_asgi.c @@ -43,8 +43,6 @@ static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx); static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args); static void nxt_python_asgi_done(void); - -int nxt_py_asgi_legacy; static PyObject *nxt_py_port_read; static nxt_unit_port_t *nxt_py_shared_port; @@ -137,6 +135,7 @@ int nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) { PyObject *func; + nxt_int_t i; PyCodeObject *code; nxt_unit_debug(NULL, "asgi_init"); @@ -161,21 +160,23 @@ nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) return NXT_UNIT_ERROR; } - func = nxt_python_asgi_get_func(nxt_py_application); - if (nxt_slow_path(func == NULL)) { - nxt_unit_alert(NULL, "Python cannot find function for callable"); - return NXT_UNIT_ERROR; - } + for (i = 0; i < nxt_py_targets->count; i++) { + func = nxt_python_asgi_get_func(nxt_py_targets->target[i].application); + if (nxt_slow_path(func == NULL)) { + nxt_unit_alert(NULL, "Python cannot find function for callable"); + return NXT_UNIT_ERROR; + } - code = (PyCodeObject *) PyFunction_GET_CODE(func); + code = (PyCodeObject *) PyFunction_GET_CODE(func); - if ((code->co_flags & CO_COROUTINE) == 0) { - nxt_unit_debug(NULL, "asgi: callable is not a coroutine function " - "switching to legacy mode"); - nxt_py_asgi_legacy = 1; - } + if ((code->co_flags & CO_COROUTINE) == 0) { + nxt_unit_debug(NULL, "asgi: callable is not a coroutine function " + "switching to legacy mode"); + nxt_py_targets->target[i].asgi_legacy = 1; + } - Py_DECREF(func); + Py_DECREF(func); + } init->callbacks.request_handler = nxt_py_asgi_request_handler; init->callbacks.data_handler = nxt_py_asgi_http_data_handler; @@ -408,6 +409,7 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req) { PyObject *scope, *res, *task, *receive, *send, *done, *asgi; PyObject *stage2; + nxt_python_target_t *target; nxt_py_asgi_ctx_data_t *ctx_data; if (req->request->websocket_handshake) { @@ -456,17 +458,18 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req) } req->data = asgi; + target = &nxt_py_targets->target[req->request->app_target]; - if (!nxt_py_asgi_legacy) { + if (!target->asgi_legacy) { nxt_unit_req_debug(req, "Python call ASGI 3.0 application"); - res = PyObject_CallFunctionObjArgs(nxt_py_application, + res = PyObject_CallFunctionObjArgs(target->application, scope, receive, send, NULL); } else { nxt_unit_req_debug(req, "Python call legacy application"); - res = PyObject_CallFunctionObjArgs(nxt_py_application, scope, NULL); + res = PyObject_CallFunctionObjArgs(target->application, scope, NULL); if (nxt_slow_path(res == NULL)) { nxt_unit_req_error(req, "Python failed to call legacy app stage1"); diff --git a/src/python/nxt_python_asgi.h b/src/python/nxt_python_asgi.h index 37f2a099..20702065 100644 --- a/src/python/nxt_python_asgi.h +++ b/src/python/nxt_python_asgi.h @@ -33,7 +33,7 @@ typedef struct { PyObject *loop_remove_reader; PyObject *quit_future; PyObject *quit_future_set_result; - PyObject *lifespan; + PyObject **target_lifespans; nxt_unit_port_t *port; } nxt_py_asgi_ctx_data_t; @@ -69,6 +69,4 @@ int nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data); int nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx); -extern int nxt_py_asgi_legacy; - #endif /* _NXT_PYTHON_ASGI_H_INCLUDED_ */ diff --git a/src/python/nxt_python_asgi_lifespan.c b/src/python/nxt_python_asgi_lifespan.c index 506eaf4d..1fc0e6b7 100644 --- a/src/python/nxt_python_asgi_lifespan.c +++ b/src/python/nxt_python_asgi_lifespan.c @@ -27,7 +27,10 @@ typedef struct { PyObject *receive_future; } nxt_py_asgi_lifespan_t; - +static PyObject *nxt_py_asgi_lifespan_target_startup( + nxt_py_asgi_ctx_data_t *ctx_data, nxt_python_target_t *target); +static int nxt_py_asgi_lifespan_target_shutdown( + nxt_py_asgi_lifespan_t *lifespan); static PyObject *nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none); static PyObject *nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict); static PyObject *nxt_py_asgi_lifespan_send_startup( @@ -69,24 +72,60 @@ static PyTypeObject nxt_py_asgi_lifespan_type = { int nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data) { - int rc; + size_t size; + PyObject *lifespan; + PyObject **target_lifespans; + nxt_int_t i; + nxt_python_target_t *target; + + size = nxt_py_targets->count * sizeof(PyObject*); + + target_lifespans = nxt_unit_malloc(NULL, size); + if (nxt_slow_path(target_lifespans == NULL)) { + nxt_unit_alert(NULL, "Failed to allocate lifespan data"); + return NXT_UNIT_ERROR; + } + + memset(target_lifespans, 0, size); + + for (i = 0; i < nxt_py_targets->count; i++) { + target = &nxt_py_targets->target[i]; + + lifespan = nxt_py_asgi_lifespan_target_startup(ctx_data, target); + if (nxt_slow_path(lifespan == NULL)) { + return NXT_UNIT_ERROR; + } + + target_lifespans[i] = lifespan; + } + + ctx_data->target_lifespans = target_lifespans; + + return NXT_UNIT_OK; +} + + +static PyObject * +nxt_py_asgi_lifespan_target_startup(nxt_py_asgi_ctx_data_t *ctx_data, + nxt_python_target_t *target) +{ PyObject *scope, *res, *py_task, *receive, *send, *done; PyObject *stage2; - nxt_py_asgi_lifespan_t *lifespan; + nxt_py_asgi_lifespan_t *lifespan, *ret; if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_lifespan_type) != 0)) { nxt_unit_alert(NULL, "Python failed to initialize the 'asgi_lifespan' type object"); - return NXT_UNIT_ERROR; + return NULL; } lifespan = PyObject_New(nxt_py_asgi_lifespan_t, &nxt_py_asgi_lifespan_type); if (nxt_slow_path(lifespan == NULL)) { nxt_unit_alert(NULL, "Python failed to create lifespan object"); - return NXT_UNIT_ERROR; + return NULL; } - rc = NXT_UNIT_ERROR; + ret = NULL; receive = PyObject_GetAttrString((PyObject *) lifespan, "receive"); if (nxt_slow_path(receive == NULL)) { @@ -130,23 +169,25 @@ nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data) goto release_future; } - if (!nxt_py_asgi_legacy) { + if (!target->asgi_legacy) { nxt_unit_req_debug(NULL, "Python call ASGI 3.0 application"); - res = PyObject_CallFunctionObjArgs(nxt_py_application, + res = PyObject_CallFunctionObjArgs(target->application, scope, receive, send, NULL); } else { nxt_unit_req_debug(NULL, "Python call legacy application"); - res = PyObject_CallFunctionObjArgs(nxt_py_application, scope, NULL); + res = PyObject_CallFunctionObjArgs(target->application, scope, NULL); if (nxt_slow_path(res == NULL)) { nxt_unit_log(NULL, NXT_UNIT_LOG_INFO, "ASGI Lifespan processing exception"); nxt_python_print_exception(); lifespan->disabled = 1; - rc = NXT_UNIT_OK; + + Py_INCREF(lifespan); + ret = lifespan; goto release_scope; } @@ -211,10 +252,9 @@ nxt_py_asgi_lifespan_startup(nxt_py_asgi_ctx_data_t *ctx_data) Py_DECREF(res); if (lifespan->startup_sent == 1 || lifespan->disabled) { - ctx_data->lifespan = (PyObject *) lifespan; - Py_INCREF(ctx_data->lifespan); + Py_INCREF(lifespan); - rc = NXT_UNIT_OK; + ret = lifespan; } release_task: @@ -232,20 +272,41 @@ release_receive: release_lifespan: Py_DECREF(lifespan); - return rc; + return (PyObject *) ret; } int nxt_py_asgi_lifespan_shutdown(nxt_unit_ctx_t *ctx) { - PyObject *msg, *future, *res; + nxt_int_t i, ret; nxt_py_asgi_lifespan_t *lifespan; nxt_py_asgi_ctx_data_t *ctx_data; ctx_data = ctx->data; - lifespan = (nxt_py_asgi_lifespan_t *) ctx_data->lifespan; + for (i = 0; i < nxt_py_targets->count; i++) { + lifespan = (nxt_py_asgi_lifespan_t *)ctx_data->target_lifespans[i]; + + ret = nxt_py_asgi_lifespan_target_shutdown(lifespan); + if (nxt_slow_path(ret != NXT_UNIT_OK)) { + return NXT_UNIT_ERROR; + } + } + + nxt_unit_free(NULL, ctx_data->target_lifespans); + + return NXT_UNIT_OK; +} + + +static int +nxt_py_asgi_lifespan_target_shutdown(nxt_py_asgi_lifespan_t *lifespan) +{ + PyObject *msg, *future, *res; + nxt_py_asgi_ctx_data_t *ctx_data; + + ctx_data = lifespan->ctx_data; if (nxt_slow_path(lifespan == NULL || lifespan->disabled)) { return NXT_UNIT_OK; diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c index 77c45af5..b80d10fa 100644 --- a/src/python/nxt_python_wsgi.c +++ b/src/python/nxt_python_wsgi.c @@ -302,7 +302,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req) { int rc; PyObject *environ, *args, *response, *iterator, *item; - PyObject *close, *result; + PyObject *close, *result, *application; nxt_bool_t prepare_environ; nxt_python_ctx_t *pctx; @@ -348,7 +348,8 @@ nxt_python_request_handler(nxt_unit_request_info_t *req) Py_INCREF(pctx->start_resp); PyTuple_SET_ITEM(args, 1, pctx->start_resp); - response = PyObject_CallObject(nxt_py_application, args); + application = nxt_py_targets->target[req->request->app_target].application; + response = PyObject_CallObject(application, args); Py_DECREF(args); -- cgit From 539551c89f4028ed70e5881400fc9151af3d12e8 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Fri, 21 May 2021 14:41:35 +0300 Subject: PHP: adopted "file_handle" to Zend API changes in 8.1.0-dev. This fixes building module with the development version of PHP after the change: https://github.com/php/php-src/commit/c732ab400af92c54eee47c487a56009f1d79dd5d --- src/nxt_php_sapi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index 23c148c8..3fb3b0db 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -1101,10 +1101,20 @@ nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) nxt_memzero(&file_handle, sizeof(file_handle)); file_handle.type = ZEND_HANDLE_FILENAME; +#if (PHP_VERSION_ID >= 80100) + file_handle.filename = zend_string_init((char *) ctx->script_filename.start, + ctx->script_filename.length, 0); + file_handle.primary_script = 1; +#else file_handle.filename = (char *) ctx->script_filename.start; +#endif php_execute_script(&file_handle TSRMLS_CC); +#if (PHP_VERSION_ID >= 80100) + zend_destroy_file_handle(&file_handle); +#endif + /* Prevention of consuming possible unread request body. */ #if (PHP_VERSION_ID < 50600) read_post = sapi_module.read_post; -- cgit From c160ea11e4ece4db52731ac8b83dd09ca2d1ef11 Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Mon, 24 May 2021 09:01:42 +0000 Subject: Node.js: renamed "require_shim" to "loader". --- src/nodejs/unit-http/loader.js | 27 +++++++++++++++++++++++++++ src/nodejs/unit-http/loader.mjs | 18 ++++++++++++++++++ src/nodejs/unit-http/require_shim.js | 27 --------------------------- src/nodejs/unit-http/require_shim.mjs | 18 ------------------ 4 files changed, 45 insertions(+), 45 deletions(-) create mode 100644 src/nodejs/unit-http/loader.js create mode 100644 src/nodejs/unit-http/loader.mjs delete mode 100644 src/nodejs/unit-http/require_shim.js delete mode 100644 src/nodejs/unit-http/require_shim.mjs (limited to 'src') diff --git a/src/nodejs/unit-http/loader.js b/src/nodejs/unit-http/loader.js new file mode 100644 index 00000000..e5aa3558 --- /dev/null +++ b/src/nodejs/unit-http/loader.js @@ -0,0 +1,27 @@ +// can only be ran as part of a --require param on the node process +if (module.parent && module.parent.id === "internal/preload") { + const { Module } = require("module") + + if (!Module.prototype.require.__unit_loader) { + const http = require("./http") + const websocket = require("./websocket") + + const original = Module.prototype.require; + + Module.prototype.require = function (id) { + switch(id) { + case "http": + case "unit-http": + return http + + case "websocket": + case "unit-http/websocket": + return websocket + } + + return original.apply(this, arguments); + } + + Module.prototype.require.__unit_loader = true; + } +} diff --git a/src/nodejs/unit-http/loader.mjs b/src/nodejs/unit-http/loader.mjs new file mode 100644 index 00000000..067d63d4 --- /dev/null +++ b/src/nodejs/unit-http/loader.mjs @@ -0,0 +1,18 @@ +// must be ran as part of a --loader or --experimental-loader param +export async function resolve(specifier, context, defaultResolver) { + switch (specifier) { + case "websocket": + return { + url: new URL("./websocket.js", import.meta.url).href, + format: "cjs" + } + + case "http": + return { + url: new URL("./http.js", import.meta.url).href, + format: "cjs" + } + } + + return defaultResolver(specifier, context, defaultResolver) +} diff --git a/src/nodejs/unit-http/require_shim.js b/src/nodejs/unit-http/require_shim.js deleted file mode 100644 index 2b307629..00000000 --- a/src/nodejs/unit-http/require_shim.js +++ /dev/null @@ -1,27 +0,0 @@ -// can only be ran as part of a --require param on the node process -if (module.parent && module.parent.id === "internal/preload") { - const { Module } = require("module") - - if (!Module.prototype.require.__unit_shim) { - const http = require("./http") - const websocket = require("./websocket") - - const original = Module.prototype.require; - - Module.prototype.require = function (id) { - switch(id) { - case "http": - case "unit-http": - return http - - case "websocket": - case "unit-http/websocket": - return websocket - } - - return original.apply(this, arguments); - } - - Module.prototype.require.__unit_shim = true; - } -} diff --git a/src/nodejs/unit-http/require_shim.mjs b/src/nodejs/unit-http/require_shim.mjs deleted file mode 100644 index 067d63d4..00000000 --- a/src/nodejs/unit-http/require_shim.mjs +++ /dev/null @@ -1,18 +0,0 @@ -// must be ran as part of a --loader or --experimental-loader param -export async function resolve(specifier, context, defaultResolver) { - switch (specifier) { - case "websocket": - return { - url: new URL("./websocket.js", import.meta.url).href, - format: "cjs" - } - - case "http": - return { - url: new URL("./http.js", import.meta.url).href, - format: "cjs" - } - } - - return defaultResolver(specifier, context, defaultResolver) -} -- cgit From 2fe76afaa69a0a992fd376c03c345f1ceb04b80e Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Tue, 25 May 2021 13:21:29 +0000 Subject: Configuration: generalized application "targets" validation. --- src/nxt_conf_validation.c | 178 ++++++++++++++++++---------------------------- 1 file changed, 68 insertions(+), 110 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index f8381cde..25f6f4fe 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -102,12 +102,6 @@ static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_python(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); -static nxt_int_t nxt_conf_vldt_python_targets_exclusive( - nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); -static nxt_int_t nxt_conf_vldt_python_targets(nxt_conf_validation_t *vldt, - nxt_conf_value_t *value, void *data); -static nxt_int_t nxt_conf_vldt_python_target(nxt_conf_validation_t *vldt, - nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_python_path_element(nxt_conf_validation_t *vldt, @@ -164,16 +158,16 @@ static nxt_int_t nxt_conf_vldt_array_iterator(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_environment(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_targets_exclusive( + nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_targets(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_target(nxt_conf_validation_t *vldt, + nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_argument(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_php(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); -static nxt_int_t nxt_conf_vldt_php_targets_exclusive( - nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); -static nxt_int_t nxt_conf_vldt_php_targets(nxt_conf_validation_t *vldt, - nxt_conf_value_t *value, void *data); -static nxt_int_t nxt_conf_vldt_php_target(nxt_conf_validation_t *vldt, - nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_php_option(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_java_classpath(nxt_conf_validation_t *vldt, @@ -210,8 +204,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[]; #endif static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_python_target_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_php_target_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[]; @@ -555,17 +551,18 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { { .name = nxt_string("module"), .type = NXT_CONF_VLDT_STRING, - .validator = nxt_conf_vldt_python_targets_exclusive, + .validator = nxt_conf_vldt_targets_exclusive, .u.string = "module", }, { .name = nxt_string("callable"), .type = NXT_CONF_VLDT_STRING, - .validator = nxt_conf_vldt_python_targets_exclusive, + .validator = nxt_conf_vldt_targets_exclusive, .u.string = "callable", }, { .name = nxt_string("targets"), .type = NXT_CONF_VLDT_OBJECT, - .validator = nxt_conf_vldt_python_targets, + .validator = nxt_conf_vldt_targets, + .u.members = nxt_conf_vldt_python_target_members }, NXT_CONF_VLDT_NEXT(nxt_conf_vldt_python_common_members) @@ -604,22 +601,23 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { { .name = nxt_string("root"), .type = NXT_CONF_VLDT_ANY_TYPE, - .validator = nxt_conf_vldt_php_targets_exclusive, + .validator = nxt_conf_vldt_targets_exclusive, .u.string = "root", }, { .name = nxt_string("script"), .type = NXT_CONF_VLDT_ANY_TYPE, - .validator = nxt_conf_vldt_php_targets_exclusive, + .validator = nxt_conf_vldt_targets_exclusive, .u.string = "script", }, { .name = nxt_string("index"), .type = NXT_CONF_VLDT_ANY_TYPE, - .validator = nxt_conf_vldt_php_targets_exclusive, + .validator = nxt_conf_vldt_targets_exclusive, .u.string = "index", }, { .name = nxt_string("targets"), .type = NXT_CONF_VLDT_OBJECT, - .validator = nxt_conf_vldt_php_targets, + .validator = nxt_conf_vldt_targets, + .u.members = nxt_conf_vldt_php_target_members }, NXT_CONF_VLDT_NEXT(nxt_conf_vldt_php_common_members) @@ -1487,52 +1485,6 @@ nxt_conf_vldt_python(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, } -static nxt_int_t -nxt_conf_vldt_python_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_python_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_python_target); -} - - -static nxt_int_t -nxt_conf_vldt_python_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 Python target name must not be empty."); - } - - if (nxt_conf_type(value) != NXT_CONF_OBJECT) { - return nxt_conf_vldt_error(vldt, "The \"%V\" Python target must be " - "an object.", name); - } - - return nxt_conf_vldt_object(vldt, value, - &nxt_conf_vldt_python_target_members); -} - - static nxt_int_t nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) @@ -2399,6 +2351,57 @@ nxt_conf_vldt_environment(nxt_conf_validation_t *vldt, nxt_str_t *name, } +static nxt_int_t +nxt_conf_vldt_targets_exclusive(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + return nxt_conf_vldt_error(vldt, "The \"%s\" option is mutually exclusive " + "with the \"targets\" object.", data); +} + + +static nxt_int_t +nxt_conf_vldt_targets(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + nxt_int_t ret; + nxt_uint_t n; + + n = nxt_conf_object_members_count(value); + + if (n > 254) { + return nxt_conf_vldt_error(vldt, "The \"targets\" object must not " + "contain more than 254 members."); + } + + vldt->ctx = data; + + ret = nxt_conf_vldt_object_iterator(vldt, value, &nxt_conf_vldt_target); + + vldt->ctx = NULL; + + return ret; +} + + +static nxt_int_t +nxt_conf_vldt_target(nxt_conf_validation_t *vldt, nxt_str_t *name, + nxt_conf_value_t *value) +{ + if (name->length == 0) { + return nxt_conf_vldt_error(vldt, + "The target name must not be empty."); + } + + if (nxt_conf_type(value) != NXT_CONF_OBJECT) { + return nxt_conf_vldt_error(vldt, "The \"%V\" target must be " + "an object.", name); + } + + return nxt_conf_vldt_object(vldt, value, vldt->ctx); +} + + static nxt_int_t nxt_conf_vldt_clone_namespaces(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) @@ -2566,51 +2569,6 @@ nxt_conf_vldt_php(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, } -static nxt_int_t -nxt_conf_vldt_php_targets_exclusive(nxt_conf_validation_t *vldt, - nxt_conf_value_t *value, void *data) -{ - return nxt_conf_vldt_error(vldt, "The \"%s\" option is mutually exclusive " - "with the \"targets\" object.", data); -} - - -static nxt_int_t -nxt_conf_vldt_php_targets(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, - void *data) -{ - nxt_uint_t n; - - n = nxt_conf_object_members_count(value); - - if (n > 254) { - return nxt_conf_vldt_error(vldt, "The \"targets\" object must not " - "contain more than 254 members."); - } - - return nxt_conf_vldt_object_iterator(vldt, value, - &nxt_conf_vldt_php_target); -} - - -static nxt_int_t -nxt_conf_vldt_php_target(nxt_conf_validation_t *vldt, nxt_str_t *name, - nxt_conf_value_t *value) -{ - if (name->length == 0) { - return nxt_conf_vldt_error(vldt, - "The PHP target name must not be empty."); - } - - if (nxt_conf_type(value) != NXT_CONF_OBJECT) { - return nxt_conf_vldt_error(vldt, "The \"%V\" PHP target must be " - "an object.", name); - } - - return nxt_conf_vldt_object(vldt, value, &nxt_conf_vldt_php_target_members); -} - - static nxt_int_t nxt_conf_vldt_php_option(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value) -- cgit From 24905c1a00dbf8f62c902d1b248279c5a31cf199 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 25 May 2021 18:01:00 +0300 Subject: Fixing racing condition on listen socket close in router (v2). This patch fixes a possible race between the nxt_router_conf_wait() and nxt_router_listen_socket_release() function calls and improves the 7f1b2eaa2d58 commit fix. --- src/nxt_router.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nxt_router.c b/src/nxt_router.c index 2bbe87b8..c597863e 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -3285,17 +3285,17 @@ nxt_router_listen_socket_close(nxt_task_t *task, void *obj, void *data) joint = lev->socket.data; lev->socket.data = NULL; + /* 'task' refers to lev->task and we cannot use after nxt_free() */ + task = &task->thread->engine->task; + + nxt_router_listen_socket_release(task, joint->socket_conf); + job = joint->close_job; job->work.next = NULL; job->work.handler = nxt_router_conf_wait; nxt_event_engine_post(job->tmcf->engine, &job->work); - /* 'task' refers to lev->task and we cannot use after nxt_free() */ - task = &task->thread->engine->task; - - nxt_router_listen_socket_release(task, joint->socket_conf); - nxt_router_listen_event_release(task, lev, joint); } -- cgit From 81e31872e3d05c912551dbf1382e3c78f4f65f4b Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Wed, 26 May 2021 16:48:05 +0000 Subject: MIME: added PHP. --- src/nxt_http_static.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index c961bb97..08f451b6 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -643,6 +643,8 @@ nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash) { nxt_string("application/octet-stream"), ".deb" }, { nxt_string("application/octet-stream"), ".rpm" }, + + { nxt_string("application/x-httpd-php"), ".php" }, }; for (i = 0; i < nxt_nitems(default_types); i++) { -- cgit From d67a0c871157454d591fa1d2a8b2d831b32e4040 Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Wed, 26 May 2021 16:48:11 +0000 Subject: Static: handled unknown MIME types when MIME-filtering active. --- src/nxt_conf_validation.c | 2 +- src/nxt_http_static.c | 32 ++++++++++++++------------------ 2 files changed, 15 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 25f6f4fe..14066fb0 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -1220,7 +1220,7 @@ nxt_conf_vldt_mtypes_extension(nxt_conf_validation_t *vldt, dup_type = nxt_http_static_mtypes_hash_find(&ctx->hash, &ext); - if (dup_type != NULL) { + if (dup_type->length != 0) { return nxt_conf_vldt_error(vldt, "The \"%V\" file extension has been " "declared for \"%V\" and \"%V\" " "MIME types at the same time.", diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index 08f451b6..c8b73fac 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -84,28 +84,22 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash, &extension); - if (mtype != NULL) { - ret = nxt_http_route_test_rule(r, action->u.share.types, - mtype->start, mtype->length); - if (ret == 1) { - goto mime_ok; - } + ret = nxt_http_route_test_rule(r, action->u.share.types, + mtype->start, mtype->length); + if (nxt_slow_path(ret == NXT_ERROR)) { + goto fail; + } - if (nxt_slow_path(ret == NXT_ERROR)) { - goto fail; + if (ret == 0) { + if (action->u.share.fallback != NULL) { + return action->u.share.fallback; } - } - if (action->u.share.fallback != NULL) { - return action->u.share.fallback; + nxt_http_request_error(task, r, NXT_HTTP_FORBIDDEN); + return NULL; } - - nxt_http_request_error(task, r, NXT_HTTP_FORBIDDEN); - return NULL; } -mime_ok: - length = action->name.length + r->path->length + index.length; fname = nxt_mp_nget(r->mem_pool, length + 1); @@ -298,7 +292,7 @@ mime_ok: &extension); } - if (mtype != NULL) { + if (mtype->length != 0) { field = nxt_list_zero_add(r->resp.fields); if (nxt_slow_path(field == NULL)) { goto fail; @@ -711,6 +705,8 @@ nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash, nxt_str_t *extension) nxt_lvlhsh_query_t lhq; nxt_http_static_mtype_t *mtype; + static nxt_str_t empty = nxt_string(""); + lhq.key = *extension; lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length); lhq.proto = &nxt_http_static_mtypes_hash_proto; @@ -720,7 +716,7 @@ nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash, nxt_str_t *extension) return mtype->type; } - return NULL; + return ∅ } -- cgit From 3efffddd95e564fe10f59e1de45afc2b551a5cba Mon Sep 17 00:00:00 2001 From: Andrey Suvorov Date: Wed, 26 May 2021 11:11:58 -0700 Subject: Fixing crash during TLS connection shutdown. A crash was caused by an incorrect timer handler nxt_h1p_idle_timeout() if SSL_shutdown() returned SSL_ERROR_WANT_READ/SSL_ERROR_WANT_WRITE. The flag SSL_RECEIVED_SHUTDOWN is used to avoid getting SSL_ERROR_WANT_READ, so the server won't wait for a close notification from a client. For SSL_ERROR_WANT_WRITE, a correct timer handler is set up. --- src/nxt_h1proto.c | 0 src/nxt_openssl.c | 32 ++++++++++++++++++++++++++------ src/nxt_router.c | 1 + src/nxt_tls.h | 2 ++ 4 files changed, 29 insertions(+), 6 deletions(-) mode change 100644 => 100755 src/nxt_h1proto.c (limited to 'src') diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c old mode 100644 new mode 100755 diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index 9b86150f..af4dc24a 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -66,6 +66,8 @@ static void nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, void *data); static nxt_int_t nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c, int ret, nxt_err_t sys_err, nxt_openssl_io_t io); +static void nxt_openssl_conn_io_shutdown_timeout(nxt_task_t *task, void *obj, + void *data); static void nxt_cdecl nxt_openssl_conn_error(nxt_task_t *task, nxt_err_t err, const char *fmt, ...); static nxt_uint_t nxt_openssl_log_error_level(nxt_err_t err); @@ -839,11 +841,7 @@ nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c) c->sendfile = NXT_CONN_SENDFILE_OFF; nxt_openssl_conn_handshake(task, c, c->socket.data); - /* - * TLS configuration might be destroyed after the TLS connection - * is established. - */ - tls->conf = NULL; + return; fail: @@ -1099,6 +1097,10 @@ nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, void *data) SSL_set_quiet_shutdown(s, quiet); + if (tls->conf->no_wait_shutdown) { + mode |= SSL_RECEIVED_SHUTDOWN; + } + once = 1; for ( ;; ) { @@ -1153,7 +1155,8 @@ nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, void *data) break; case NXT_AGAIN: - nxt_timer_add(task->thread->engine, &c->read_timer, 5000); + c->write_timer.handler = nxt_openssl_conn_io_shutdown_timeout; + nxt_timer_add(task->thread->engine, &c->write_timer, 5000); return; default: @@ -1237,6 +1240,23 @@ nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c, int ret, } +static void +nxt_openssl_conn_io_shutdown_timeout(nxt_task_t *task, void *obj, void *data) +{ + nxt_conn_t *c; + nxt_timer_t *timer; + + timer = obj; + + nxt_debug(task, "openssl conn shutdown timeout"); + + c = nxt_write_timer_conn(timer); + + c->socket.timedout = 1; + nxt_openssl_conn_io_shutdown(task, c, NULL); +} + + static void nxt_cdecl nxt_openssl_conn_error(nxt_task_t *task, nxt_err_t err, const char *fmt, ...) { diff --git a/src/nxt_router.c b/src/nxt_router.c index c597863e..122fd523 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -2477,6 +2477,7 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, goto fail; } + tlscf->no_wait_shutdown = 1; rpc->socket_conf->tls = tlscf; } else { diff --git a/src/nxt_tls.h b/src/nxt_tls.h index c44bfe56..2a29f3ca 100644 --- a/src/nxt_tls.h +++ b/src/nxt_tls.h @@ -69,6 +69,8 @@ struct nxt_tls_conf_s { char *ca_certificate; size_t buffer_size; + + uint8_t no_wait_shutdown; /* 1 bit */ }; -- cgit From 3f7ccf142ff4d1a11b807a344bcb1e3cb6c3284b Mon Sep 17 00:00:00 2001 From: Andrey Suvorov Date: Wed, 26 May 2021 11:19:47 -0700 Subject: Enabling SSL_CTX configuration by using SSL_CONF_cmd(). To perform various configuration operations on SSL_CTX, OpenSSL provides SSL_CONF_cmd(). Specifically, to configure ciphers for a listener, "CipherString" and "Ciphersuites" file commands are used: https://www.openssl.org/docs/man1.1.1/man3/SSL_CONF_cmd.html This feature can be configured in the "tls/conf_commands" section. --- src/nxt_conf_validation.c | 46 +++++++++++++++++++ src/nxt_openssl.c | 114 ++++++++++++++++++++++++++++++++++++++++++---- src/nxt_router.c | 90 ++++++++++++++++-------------------- src/nxt_tls.h | 6 ++- 4 files changed, 195 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 14066fb0..06ae2847 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -89,6 +89,10 @@ static nxt_int_t nxt_conf_vldt_listener(nxt_conf_validation_t *vldt, #if (NXT_TLS) static nxt_int_t nxt_conf_vldt_certificate(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +#if (NXT_HAVE_OPENSSL_CONF_CMD) +static nxt_int_t nxt_conf_vldt_object_conf_commands(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +#endif static nxt_int_t nxt_conf_vldt_certificate_element(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); #endif @@ -363,7 +367,17 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = { { .name = nxt_string("certificate"), .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, + .flags = NXT_CONF_VLDT_REQUIRED, .validator = nxt_conf_vldt_certificate, + }, { + .name = nxt_string("conf_commands"), + .type = NXT_CONF_VLDT_OBJECT, +#if (NXT_HAVE_OPENSSL_CONF_CMD) + .validator = nxt_conf_vldt_object_conf_commands, +#else + .validator = nxt_conf_vldt_unsupported, + .u.string = "conf_commands", +#endif }, NXT_CONF_VLDT_END @@ -1971,6 +1985,38 @@ nxt_conf_vldt_certificate_element(nxt_conf_validation_t *vldt, return NXT_OK; } + +#if (NXT_HAVE_OPENSSL_CONF_CMD) + +static nxt_int_t +nxt_conf_vldt_object_conf_commands(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + uint32_t index; + nxt_int_t ret; + nxt_str_t name; + nxt_conf_value_t *member; + + index = 0; + + for ( ;; ) { + member = nxt_conf_next_object_member(value, &name, &index); + + if (member == NULL) { + break; + } + + ret = nxt_conf_vldt_type(vldt, &name, member, NXT_CONF_VLDT_STRING); + if (ret != NXT_OK) { + return ret; + } + } + + return NXT_OK; +} + +#endif + #endif diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index af4dc24a..2fd5d1bf 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -42,9 +43,14 @@ static unsigned long nxt_openssl_thread_id(void); static void nxt_openssl_locks_free(void); #endif static nxt_int_t nxt_openssl_server_init(nxt_task_t *task, - nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t last); + nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_conf_value_t *conf_cmds, + nxt_bool_t last); static nxt_int_t nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t single); +#if (NXT_HAVE_OPENSSL_CONF_CMD) +static nxt_int_t nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx, + nxt_conf_value_t *value, nxt_mp_t *mp); +#endif static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf, nxt_mp_t *mp); static nxt_int_t nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq, @@ -260,7 +266,7 @@ nxt_openssl_locks_free(void) static nxt_int_t nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf, - nxt_mp_t *mp, nxt_bool_t last) + nxt_mp_t *mp, nxt_conf_value_t *conf_cmds, nxt_bool_t last) { SSL_CTX *ctx; const char *ciphers, *ca_certificate; @@ -320,6 +326,7 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf, goto fail; } */ + ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5"; if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) { @@ -329,6 +336,14 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf, goto fail; } +#if (NXT_HAVE_OPENSSL_CONF_CMD) + if (conf_cmds != NULL + && nxt_ssl_conf_commands(task, ctx, conf_cmds, mp) != NXT_OK) + { + goto fail; + } +#endif + SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); if (conf->ca_certificate != NULL) { @@ -484,6 +499,89 @@ clean: } +#if (NXT_HAVE_OPENSSL_CONF_CMD) + +static nxt_int_t +nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx, nxt_conf_value_t *conf, + nxt_mp_t *mp) +{ + int ret; + char *zcmd, *zvalue; + uint32_t index; + nxt_str_t cmd, value; + SSL_CONF_CTX *cctx; + nxt_conf_value_t *member; + + cctx = SSL_CONF_CTX_new(); + if (nxt_slow_path(cctx == NULL)) { + nxt_openssl_log_error(task, NXT_LOG_ALERT, + "SSL_CONF_CTX_new() failed"); + return NXT_ERROR; + } + + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE + | SSL_CONF_FLAG_SERVER + | SSL_CONF_FLAG_CERTIFICATE + | SSL_CONF_FLAG_SHOW_ERRORS); + + SSL_CONF_CTX_set_ssl_ctx(cctx, ctx); + + index = 0; + + for ( ;; ) { + member = nxt_conf_next_object_member(conf, &cmd, &index); + if (nxt_slow_path(member == NULL)) { + break; + } + + nxt_conf_get_string(member, &value); + + zcmd = nxt_str_cstrz(mp, &cmd); + zvalue = nxt_str_cstrz(mp, &value); + + if (nxt_slow_path(zcmd == NULL || zvalue == NULL)) { + goto fail; + } + + ret = SSL_CONF_cmd(cctx, zcmd, zvalue); + if (ret == -2) { + nxt_openssl_log_error(task, NXT_LOG_ERR, + "unknown command \"%s\" in " + "\"conf_commands\" option", zcmd); + goto fail; + } + + if (ret <= 0) { + nxt_openssl_log_error(task, NXT_LOG_ERR, + "invalid value \"%s\" for command \"%s\" " + "in \"conf_commands\" option", + zvalue, zcmd); + goto fail; + } + + nxt_debug(task, "SSL_CONF_cmd(\"%s\", \"%s\")", zcmd, zvalue); + } + + if (SSL_CONF_CTX_finish(cctx) != 1) { + nxt_openssl_log_error(task, NXT_LOG_ALERT, + "SSL_CONF_finish() failed"); + goto fail; + } + + SSL_CONF_CTX_free(cctx); + + return NXT_OK; + +fail: + + SSL_CONF_CTX_free(cctx); + + return NXT_ERROR; +} + +#endif + + static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf, nxt_mp_t *mp) @@ -550,7 +648,7 @@ nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf, NULL, 0); if (len <= 0) { nxt_log(task, NXT_LOG_WARN, "certificate \"%V\" has neither " - "Subject Alternative Name nor Common Name", bundle->name); + "Subject Alternative Name nor Common Name", &bundle->name); return NXT_OK; } @@ -629,7 +727,7 @@ nxt_openssl_bundle_hash_insert(nxt_task_t *task, nxt_lvlhsh_t *lvlhsh, if (item->name.length == 0 || item->name.start[0] != '.') { nxt_log(task, NXT_LOG_WARN, "ignored invalid name \"%V\" " "in certificate \"%V\": missing \".\" " - "after wildcard symbol", &str, item->bundle->name); + "after wildcard symbol", &str, &item->bundle->name); return NXT_OK; } } @@ -644,7 +742,7 @@ nxt_openssl_bundle_hash_insert(nxt_task_t *task, nxt_lvlhsh_t *lvlhsh, ret = nxt_lvlhsh_insert(lvlhsh, &lhq); if (nxt_fast_path(ret == NXT_OK)) { nxt_debug(task, "name \"%V\" for certificate \"%V\" is inserted", - &str, item->bundle->name); + &str, &item->bundle->name); return NXT_OK; } @@ -653,7 +751,7 @@ nxt_openssl_bundle_hash_insert(nxt_task_t *task, nxt_lvlhsh_t *lvlhsh, if (old->bundle != item->bundle) { nxt_log(task, NXT_LOG_WARN, "ignored duplicate name \"%V\" " "in certificate \"%V\", identical name appears in \"%V\"", - &str, old->bundle->name, item->bundle->name); + &str, &old->bundle->name, &item->bundle->name); old->bundle = item->bundle; } @@ -730,8 +828,8 @@ nxt_openssl_servername(SSL *s, int *ad, void *arg) if (bundle != NULL) { nxt_debug(c->socket.task, "new tls context found for \"%V\": \"%V\" " - "(old: \"%V\")", &str, bundle->name, - conf->bundle->name); + "(old: \"%V\")", &str, &bundle->name, + &conf->bundle->name); if (bundle != conf->bundle) { if (SSL_set_SSL_CTX(s, bundle->ctx) == NULL) { diff --git a/src/nxt_router.c b/src/nxt_router.c index 122fd523..015ae226 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -41,8 +41,11 @@ typedef struct { #if (NXT_TLS) typedef struct { - nxt_str_t name; - nxt_socket_conf_t *conf; + nxt_str_t name; + nxt_socket_conf_t *socket_conf; + nxt_router_temp_conf_t *temp_conf; + nxt_conf_value_t *conf_cmds; + nxt_bool_t last; nxt_queue_link_t link; /* for nxt_socket_conf_t.tls */ } nxt_router_tlssock_t; @@ -117,12 +120,11 @@ static void nxt_router_listen_socket_ready(nxt_task_t *task, static void nxt_router_listen_socket_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); #if (NXT_TLS) -static void nxt_router_tls_rpc_create(nxt_task_t *task, - nxt_router_temp_conf_t *tmcf, nxt_router_tlssock_t *tls, nxt_bool_t last); static void nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); static nxt_int_t nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf, - nxt_conf_value_t *value, nxt_socket_conf_t *skcf); + nxt_conf_value_t *value, nxt_socket_conf_t *skcf, + nxt_conf_value_t * conf_cmds); #endif static void nxt_router_app_rpc_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_app_t *app); @@ -954,8 +956,10 @@ nxt_router_conf_apply(nxt_task_t *task, void *obj, void *data) tls = nxt_queue_link_data(qlk, nxt_router_tlssock_t, link); - nxt_router_tls_rpc_create(task, tmcf, tls, - nxt_queue_is_empty(&tmcf->tls)); + tls->last = nxt_queue_is_empty(&tmcf->tls); + + nxt_cert_store_get(task, &tls->name, tmcf->mem_pool, + nxt_router_tls_rpc_handler, tls); return; } #endif @@ -1337,7 +1341,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_router_t *router; nxt_app_joint_t *app_joint; #if (NXT_TLS) - nxt_conf_value_t *certificate; + nxt_conf_value_t *certificate, *conf_cmds; #endif nxt_conf_value_t *conf, *http, *value, *websocket; nxt_conf_value_t *applications, *application; @@ -1358,6 +1362,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, static nxt_str_t access_log_path = nxt_string("/access_log"); #if (NXT_TLS) static nxt_str_t certificate_path = nxt_string("/tls/certificate"); + static nxt_str_t conf_commands_path = nxt_string("/tls/conf_commands"); #endif static nxt_str_t static_path = nxt_string("/settings/http/static"); static nxt_str_t websocket_path = nxt_string("/settings/http/websocket"); @@ -1736,6 +1741,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, certificate = nxt_conf_get_path(listener, &certificate_path); if (certificate != NULL) { + conf_cmds = nxt_conf_get_path(listener, &conf_commands_path); + if (nxt_conf_type(certificate) == NXT_CONF_ARRAY) { n = nxt_conf_array_elements_count(certificate); @@ -1744,7 +1751,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_assert(value != NULL); - ret = nxt_router_conf_tls_insert(tmcf, value, skcf); + ret = nxt_router_conf_tls_insert(tmcf, value, skcf, + conf_cmds); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } @@ -1752,7 +1760,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, } else { /* NXT_CONF_STRING */ - ret = nxt_router_conf_tls_insert(tmcf, certificate, skcf); + ret = nxt_router_conf_tls_insert(tmcf, certificate, skcf, + conf_cmds); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } @@ -1846,25 +1855,20 @@ fail: static nxt_int_t nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf, - nxt_conf_value_t *value, nxt_socket_conf_t *skcf) + nxt_conf_value_t *value, nxt_socket_conf_t *skcf, + nxt_conf_value_t *conf_cmds) { - nxt_mp_t *mp; - nxt_str_t str; nxt_router_tlssock_t *tls; - mp = tmcf->router_conf->mem_pool; - - tls = nxt_mp_get(mp, sizeof(nxt_router_tlssock_t)); + tls = nxt_mp_get(tmcf->mem_pool, sizeof(nxt_router_tlssock_t)); if (nxt_slow_path(tls == NULL)) { return NXT_ERROR; } - tls->conf = skcf; - nxt_conf_get_string(value, &str); - - if (nxt_slow_path(nxt_str_dup(mp, &tls->name, &str) == NULL)) { - return NXT_ERROR; - } + tls->socket_conf = skcf; + tls->conf_cmds = conf_cmds; + tls->temp_conf = tmcf; + nxt_conf_get_string(value, &tls->name); nxt_queue_insert_tail(&tmcf->tls, &tls->link); @@ -2427,28 +2431,6 @@ nxt_router_listen_socket_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, #if (NXT_TLS) -static void -nxt_router_tls_rpc_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, - nxt_router_tlssock_t *tls, nxt_bool_t last) -{ - nxt_socket_rpc_t *rpc; - - rpc = nxt_mp_alloc(tmcf->mem_pool, sizeof(nxt_socket_rpc_t)); - if (rpc == NULL) { - nxt_router_conf_error(task, tmcf); - return; - } - - rpc->name = &tls->name; - rpc->socket_conf = tls->conf; - rpc->temp_conf = tmcf; - rpc->last = last; - - nxt_cert_store_get(task, &tls->name, tmcf->mem_pool, - nxt_router_tls_rpc_handler, rpc); -} - - static void nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) @@ -2456,14 +2438,14 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, nxt_mp_t *mp; nxt_int_t ret; nxt_tls_conf_t *tlscf; - nxt_socket_rpc_t *rpc; + nxt_router_tlssock_t *tls; nxt_tls_bundle_conf_t *bundle; nxt_router_temp_conf_t *tmcf; nxt_debug(task, "tls rpc handler"); - rpc = data; - tmcf = rpc->temp_conf; + tls = data; + tmcf = tls->temp_conf; if (msg == NULL || msg->port_msg.type == _NXT_PORT_MSG_RPC_ERROR) { goto fail; @@ -2471,17 +2453,17 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, mp = tmcf->router_conf->mem_pool; - if (rpc->socket_conf->tls == NULL){ + if (tls->socket_conf->tls == NULL){ tlscf = nxt_mp_zget(mp, sizeof(nxt_tls_conf_t)); if (nxt_slow_path(tlscf == NULL)) { goto fail; } tlscf->no_wait_shutdown = 1; - rpc->socket_conf->tls = tlscf; + tls->socket_conf->tls = tlscf; } else { - tlscf = rpc->socket_conf->tls; + tlscf = tls->socket_conf->tls; } bundle = nxt_mp_get(mp, sizeof(nxt_tls_bundle_conf_t)); @@ -2489,12 +2471,16 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, goto fail; } - bundle->name = rpc->name; + if (nxt_slow_path(nxt_str_dup(mp, &bundle->name, &tls->name) == NULL)) { + goto fail; + } + bundle->chain_file = msg->fd[0]; bundle->next = tlscf->bundle; tlscf->bundle = bundle; - ret = task->thread->runtime->tls->server_init(task, tlscf, mp, rpc->last); + ret = task->thread->runtime->tls->server_init(task, tlscf, mp, + tls->conf_cmds, tls->last); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } diff --git a/src/nxt_tls.h b/src/nxt_tls.h index 2a29f3ca..63c49ee4 100644 --- a/src/nxt_tls.h +++ b/src/nxt_tls.h @@ -8,6 +8,9 @@ #define _NXT_TLS_H_INCLUDED_ +#include + + /* * The SSL/TLS libraries lack vector I/O interface yet add noticeable * overhead to each SSL/TLS record so buffering allows to decrease the @@ -32,6 +35,7 @@ typedef struct { nxt_int_t (*server_init)(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_mp_t *mp, + nxt_conf_value_t *conf_cmds, nxt_bool_t last); void (*server_free)(nxt_task_t *task, nxt_tls_conf_t *conf); @@ -49,7 +53,7 @@ struct nxt_tls_bundle_conf_s { void *ctx; nxt_fd_t chain_file; - nxt_str_t *name; + nxt_str_t name; nxt_tls_bundle_conf_t *next; }; -- cgit