diff options
author | Andrei Belov <defan@nginx.com> | 2021-08-19 18:17:12 +0300 |
---|---|---|
committer | Andrei Belov <defan@nginx.com> | 2021-08-19 18:17:12 +0300 |
commit | db442f1be7e713e6a219621ff97a51046590dbd6 (patch) | |
tree | 913734275bc890ec175e51fcb0f36b01a3c52c24 /src | |
parent | a1d2ced6fc2317d36bc917c5d0ac339bc647dc34 (diff) | |
parent | 13c0025dfa6e041563d0ad5dd81679b44522694c (diff) | |
download | unit-db442f1be7e713e6a219621ff97a51046590dbd6.tar.gz unit-db442f1be7e713e6a219621ff97a51046590dbd6.tar.bz2 |
Merged with the default branch.1.25.0-1
Diffstat (limited to 'src')
33 files changed, 2013 insertions, 600 deletions
diff --git a/src/nxt_application.h b/src/nxt_application.h index 45e7fa48..6fbdc4be 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -74,6 +74,7 @@ typedef struct { typedef struct { nxt_str_t script; uint32_t threads; + nxt_str_t hooks; } nxt_ruby_app_conf_t; diff --git a/src/nxt_buf.c b/src/nxt_buf.c index 83be0fac..cbde069e 100644 --- a/src/nxt_buf.c +++ b/src/nxt_buf.c @@ -201,7 +201,6 @@ nxt_buf_completion(nxt_task_t *task, void *obj, void *data) nxt_buf_t *b, *next, *parent; b = obj; - parent = data; nxt_debug(task, "buf completion: %p %p", b, b->mem.start); @@ -275,7 +274,6 @@ nxt_buf_ts_completion(nxt_task_t *task, void *obj, void *data) nxt_buf_t *b, *next, *parent; b = obj; - parent = data; if (nxt_buf_ts_handle(task, obj, data)) { return; diff --git a/src/nxt_buf.h b/src/nxt_buf.h index 25e8499a..5121d659 100644 --- a/src/nxt_buf.h +++ b/src/nxt_buf.h @@ -288,4 +288,10 @@ nxt_buf_cpystr(nxt_buf_t *b, const nxt_str_t *str) } +nxt_inline void +nxt_buf_dummy_completion(nxt_task_t *task, void *obj, void *data) +{ +} + + #endif /* _NXT_BUF_H_INCLIDED_ */ diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 06ae2847..a53fff74 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -95,6 +95,16 @@ static nxt_int_t nxt_conf_vldt_object_conf_commands(nxt_conf_validation_t *vldt, #endif static nxt_int_t nxt_conf_vldt_certificate_element(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_tls_cache_size(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_tls_timeout(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +#if (NXT_HAVE_OPENSSL_TLSEXT) +static nxt_int_t nxt_conf_vldt_ticket_key(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value); +#endif #endif static nxt_int_t nxt_conf_vldt_action(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); @@ -204,8 +214,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_client_ip_members[]; #if (NXT_TLS) static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_session_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[]; @@ -346,6 +358,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = { .name = nxt_string("application"), .type = NXT_CONF_VLDT_STRING, .validator = nxt_conf_vldt_app_name, + }, { + .name = nxt_string("client_ip"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_client_ip_members }, #if (NXT_TLS) @@ -361,6 +378,25 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = { }; +static nxt_conf_vldt_object_t nxt_conf_vldt_client_ip_members[] = { + { + .name = nxt_string("source"), + .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, + .validator = nxt_conf_vldt_match_addrs, + .flags = NXT_CONF_VLDT_REQUIRED + }, { + .name = nxt_string("header"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED + }, { + .name = nxt_string("recursive"), + .type = NXT_CONF_VLDT_BOOLEAN, + }, + + NXT_CONF_VLDT_END +}; + + #if (NXT_TLS) static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = { @@ -378,11 +414,132 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = { .validator = nxt_conf_vldt_unsupported, .u.string = "conf_commands", #endif + }, { + .name = nxt_string("session"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_session_members, + }, + + NXT_CONF_VLDT_END +}; + + +static nxt_conf_vldt_object_t nxt_conf_vldt_session_members[] = { + { + .name = nxt_string("cache_size"), + .type = NXT_CONF_VLDT_INTEGER, + .validator = nxt_conf_vldt_tls_cache_size, + }, { + .name = nxt_string("timeout"), + .type = NXT_CONF_VLDT_INTEGER, + .validator = nxt_conf_vldt_tls_timeout, + }, { + .name = nxt_string("tickets"), + .type = NXT_CONF_VLDT_STRING + | NXT_CONF_VLDT_ARRAY + | NXT_CONF_VLDT_BOOLEAN, +#if (NXT_HAVE_OPENSSL_TLSEXT) + .validator = nxt_conf_vldt_ticket_key, +#else + .validator = nxt_conf_vldt_unsupported, + .u.string = "tickets", +#endif }, NXT_CONF_VLDT_END }; + +static nxt_int_t +nxt_conf_vldt_tls_cache_size(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + int64_t cache_size; + + cache_size = nxt_conf_get_number(value); + + if (cache_size < 0) { + return nxt_conf_vldt_error(vldt, "The \"cache_size\" number must not " + "be negative."); + } + + return NXT_OK; +} + + +static nxt_int_t +nxt_conf_vldt_tls_timeout(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + int64_t timeout; + + timeout = nxt_conf_get_number(value); + + if (timeout <= 0) { + return nxt_conf_vldt_error(vldt, "The \"timeout\" number must be " + "greater than zero."); + } + + return NXT_OK; +} + +#endif + +#if (NXT_HAVE_OPENSSL_TLSEXT) + +static nxt_int_t +nxt_conf_vldt_ticket_key(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + if (nxt_conf_type(value) == NXT_CONF_BOOLEAN) { + return NXT_OK; + } + + if (nxt_conf_type(value) == NXT_CONF_ARRAY) { + return nxt_conf_vldt_array_iterator(vldt, value, + &nxt_conf_vldt_ticket_key_element); + } + + /* NXT_CONF_STRING */ + + return nxt_conf_vldt_ticket_key_element(vldt, value); +} + + +static nxt_int_t +nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value) +{ + nxt_str_t key; + nxt_int_t ret; + + if (nxt_conf_type(value) != NXT_CONF_STRING) { + return nxt_conf_vldt_error(vldt, "The \"key\" array must " + "contain only string values."); + } + + nxt_conf_get_string(value, &key); + + ret = nxt_openssl_base64_decode(NULL, 0, key.start, key.length); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } + + if (ret == NXT_DECLINED) { + return nxt_conf_vldt_error(vldt, "Invalid Base64 format for the ticket " + "key \"%V\".", &key); + } + + if (ret != 48 && ret != 80) { + return nxt_conf_vldt_error(vldt, "Invalid length %d of the ticket " + "key \"%V\". Must be 48 or 80 bytes.", + ret, &key); + } + + return NXT_OK; +} + #endif @@ -732,6 +889,9 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_ruby_members[] = { .name = nxt_string("threads"), .type = NXT_CONF_VLDT_INTEGER, .validator = nxt_conf_vldt_threads, + }, { + .name = nxt_string("hooks"), + .type = NXT_CONF_VLDT_STRING }, NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) @@ -1215,7 +1375,7 @@ static nxt_int_t nxt_conf_vldt_mtypes_extension(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) { - nxt_str_t ext, *dup_type; + nxt_str_t exten, *dup_type; nxt_conf_vldt_mtypes_ctx_t *ctx; ctx = vldt->ctx; @@ -1225,24 +1385,24 @@ nxt_conf_vldt_mtypes_extension(nxt_conf_validation_t *vldt, "contain only strings.", ctx->type); } - nxt_conf_get_string(value, &ext); + nxt_conf_get_string(value, &exten); - if (ext.length == 0) { + if (exten.length == 0) { return nxt_conf_vldt_error(vldt, "An empty file extension for " "the \"%V\" MIME type.", ctx->type); } - dup_type = nxt_http_static_mtypes_hash_find(&ctx->hash, &ext); + dup_type = nxt_http_static_mtype_get(&ctx->hash, &exten); 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.", - &ext, dup_type, ctx->type); + &exten, dup_type, ctx->type); } - return nxt_http_static_mtypes_hash_add(ctx->pool, &ctx->hash, - &ext, ctx->type); + return nxt_http_static_mtypes_hash_add(ctx->pool, &ctx->hash, &exten, + ctx->type); } diff --git a/src/nxt_controller.c b/src/nxt_controller.c index 772d10c8..779a625d 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -92,6 +92,10 @@ static nxt_bool_t nxt_controller_cert_in_use(nxt_str_t *name); static void nxt_controller_cert_cleanup(nxt_task_t *task, void *obj, void *data); #endif +static void nxt_controller_process_control(nxt_task_t *task, + nxt_controller_request_t *req, nxt_str_t *path); +static void nxt_controller_app_restart_handler(nxt_task_t *task, + nxt_port_recv_msg_t *msg, void *data); static void nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); static void nxt_controller_conf_store(nxt_task_t *task, @@ -1022,6 +1026,14 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) #endif + if (nxt_str_start(&path, "/control/", 9)) { + path.length -= 9; + path.start += 9; + + nxt_controller_process_control(task, req, &path); + return; + } + nxt_memzero(&resp, sizeof(nxt_controller_response_t)); if (path.length == 1 && path.start[0] == '/') { @@ -1684,6 +1696,155 @@ nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, static void +nxt_controller_process_control(nxt_task_t *task, + nxt_controller_request_t *req, nxt_str_t *path) +{ + uint32_t stream; + nxt_buf_t *b; + nxt_int_t rc; + nxt_port_t *router_port, *controller_port; + nxt_runtime_t *rt; + nxt_conf_value_t *value; + nxt_controller_response_t resp; + + static nxt_str_t applications = nxt_string("applications"); + + nxt_memzero(&resp, sizeof(nxt_controller_response_t)); + + if (!nxt_str_eq(&req->parser.method, "GET", 3)) { + goto not_allowed; + } + + if (!nxt_str_start(path, "applications/", 13) + || nxt_memcmp(path->start + path->length - 8, "/restart", 8) != 0) + { + goto not_found; + } + + path->start += 13; + path->length -= 13 + 8; + + if (nxt_controller_check_postpone_request(task)) { + nxt_queue_insert_tail(&nxt_controller_waiting_requests, &req->link); + return; + } + + value = nxt_controller_conf.root; + if (value == NULL) { + goto not_found; + } + + value = nxt_conf_get_object_member(value, &applications, NULL); + if (value == NULL) { + goto not_found; + } + + value = nxt_conf_get_object_member(value, path, NULL); + if (value == NULL) { + goto not_found; + } + + b = nxt_buf_mem_alloc(req->conn->mem_pool, path->length, 0); + if (nxt_slow_path(b == NULL)) { + goto alloc_fail; + } + + b->mem.free = nxt_cpymem(b->mem.pos, path->start, path->length); + + rt = task->thread->runtime; + + controller_port = rt->port_by_type[NXT_PROCESS_CONTROLLER]; + router_port = rt->port_by_type[NXT_PROCESS_ROUTER]; + + stream = nxt_port_rpc_register_handler(task, controller_port, + nxt_controller_app_restart_handler, + nxt_controller_app_restart_handler, + router_port->pid, req); + if (nxt_slow_path(stream == 0)) { + goto alloc_fail; + } + + rc = nxt_port_socket_write(task, router_port, NXT_PORT_MSG_APP_RESTART, + -1, stream, 0, b); + if (nxt_slow_path(rc != NXT_OK)) { + nxt_port_rpc_cancel(task, controller_port, stream); + + goto fail; + } + + nxt_queue_insert_head(&nxt_controller_waiting_requests, &req->link); + + return; + +not_allowed: + + resp.status = 405; + resp.title = (u_char *) "Method isn't allowed."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); + return; + +not_found: + + resp.status = 404; + resp.title = (u_char *) "Value doesn't exist."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); + return; + +alloc_fail: + + resp.status = 500; + resp.title = (u_char *) "Memory allocation failed."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); + return; + +fail: + + resp.status = 500; + resp.title = (u_char *) "Send restart failed."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); +} + + +static void +nxt_controller_app_restart_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, + void *data) +{ + nxt_controller_request_t *req; + nxt_controller_response_t resp; + + req = data; + + nxt_debug(task, "controller app restart handler"); + + nxt_queue_remove(&req->link); + + nxt_memzero(&resp, sizeof(nxt_controller_response_t)); + + if (msg->port_msg.type == NXT_PORT_MSG_RPC_READY) { + resp.status = 200; + resp.title = (u_char *) "Ok"; + + } else { + resp.status = 500; + resp.title = (u_char *) "Failed to restart app."; + resp.offset = -1; + } + + nxt_controller_response(task, req, &resp); + + nxt_controller_flush_requests(task); +} + + +static void nxt_controller_conf_store(nxt_task_t *task, nxt_conf_value_t *conf) { void *mem; diff --git a/src/nxt_event_engine.c b/src/nxt_event_engine.c index 4384d3b1..78c79bb1 100644 --- a/src/nxt_event_engine.c +++ b/src/nxt_event_engine.c @@ -720,11 +720,10 @@ nxt_event_engine_buf_mem_free(nxt_event_engine_t *engine, nxt_buf_t *b) void nxt_event_engine_buf_mem_completion(nxt_task_t *task, void *obj, void *data) { - nxt_event_engine_t *engine; nxt_buf_t *b, *next, *parent; + nxt_event_engine_t *engine; b = obj; - parent = data; nxt_debug(task, "buf completion: %p %p", b, b->mem.start); diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c index d3da6942..b683cb22 100755 --- a/src/nxt_h1proto.c +++ b/src/nxt_h1proto.c @@ -955,7 +955,6 @@ nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r) } else { size = nxt_min(body_buffer_size, size); b->mem.free = nxt_cpymem(b->mem.free, in->mem.pos, size); - body_buffer_size -= size; } in->mem.pos += size; diff --git a/src/nxt_http.h b/src/nxt_http.h index f82d837e..3bc2fd61 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -197,8 +197,23 @@ struct nxt_http_request_s { }; -typedef struct nxt_http_route_s nxt_http_route_t; -typedef struct nxt_http_route_rule_s nxt_http_route_rule_t; +typedef struct nxt_http_route_s nxt_http_route_t; +typedef struct nxt_http_route_rule_s nxt_http_route_rule_t; +typedef struct nxt_http_route_addr_rule_s nxt_http_route_addr_rule_t; + + +typedef struct { + nxt_conf_value_t *pass; + nxt_conf_value_t *ret; + nxt_str_t location; + nxt_conf_value_t *proxy; + nxt_conf_value_t *share; + nxt_str_t chroot; + nxt_conf_value_t *follow_symlinks; + nxt_conf_value_t *traverse_mounts; + nxt_conf_value_t *types; + nxt_conf_value_t *fallback; +} nxt_http_action_conf_t; struct nxt_http_action_s { @@ -206,26 +221,15 @@ struct nxt_http_action_s { nxt_http_request_t *r, nxt_http_action_t *action); union { + void *conf; nxt_http_route_t *route; nxt_upstream_t *upstream; uint32_t upstream_number; - nxt_http_status_t return_code; nxt_var_t *var; - - struct { - nxt_app_t *application; - nxt_int_t target; - } app; - - struct { - nxt_str_t chroot; - nxt_uint_t resolve; - nxt_http_route_rule_t *types; - nxt_http_action_t *fallback; - } share; } u; nxt_str_t name; + nxt_http_action_t *fallback; }; @@ -251,6 +255,14 @@ typedef struct { } nxt_http_proto_table_t; +struct nxt_http_client_ip_s { + nxt_http_route_addr_rule_t *source; + nxt_str_t *header; + uint32_t header_hash; + uint8_t recursive; /* 1 bit */ +}; + + #define NXT_HTTP_DATE_LEN nxt_length("Wed, 31 Dec 1986 16:40:00 GMT") nxt_inline u_char * @@ -308,27 +320,34 @@ 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_http_route_addr_rule_t *nxt_http_route_addr_rule_create( + nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv); +nxt_int_t nxt_http_route_addr_rule(nxt_http_request_t *r, + nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sockaddr); +nxt_http_route_rule_t *nxt_http_route_types_rule_create(nxt_task_t *task, + nxt_mp_t *mp, nxt_conf_value_t *types); 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_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_conf_value_t *cv, nxt_http_action_t *action); +void nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r, + nxt_http_action_t *action); + nxt_int_t nxt_upstreams_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *conf); nxt_int_t nxt_upstreams_joint_create(nxt_router_temp_conf_t *tmcf, nxt_upstream_t ***upstream_joint); -void nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r, - nxt_http_action_t *action); - -nxt_http_action_t *nxt_http_return_handler(nxt_task_t *task, - nxt_http_request_t *r, nxt_http_action_t *action); +nxt_int_t nxt_http_return_init(nxt_mp_t *mp, nxt_http_action_t *action, + nxt_http_action_conf_t *acf); -nxt_http_action_t *nxt_http_static_handler(nxt_task_t *task, - nxt_http_request_t *r, nxt_http_action_t *action); +nxt_int_t nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_http_action_t *action, nxt_http_action_conf_t *acf); nxt_int_t nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash); nxt_int_t nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash, - nxt_str_t *extension, nxt_str_t *type); -nxt_str_t *nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash, - nxt_str_t *extension); + nxt_str_t *exten, nxt_str_t *type); +nxt_str_t *nxt_http_static_mtype_get(nxt_lvlhsh_t *hash, nxt_str_t *exten); nxt_http_action_t *nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action); @@ -337,8 +356,8 @@ nxt_int_t nxt_upstream_find(nxt_upstreams_t *upstreams, nxt_str_t *name, nxt_http_action_t *nxt_upstream_proxy_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_upstream_t *upstream); - -nxt_int_t nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action); +nxt_int_t nxt_http_proxy_init(nxt_mp_t *mp, nxt_http_action_t *action, + nxt_http_action_conf_t *acf); nxt_int_t nxt_http_proxy_date(void *ctx, nxt_http_field_t *field, uintptr_t data); nxt_int_t nxt_http_proxy_content_length(void *ctx, nxt_http_field_t *field, diff --git a/src/nxt_http_chunk_parse.c b/src/nxt_http_chunk_parse.c index be3a2023..deab116d 100644 --- a/src/nxt_http_chunk_parse.c +++ b/src/nxt_http_chunk_parse.c @@ -253,7 +253,6 @@ nxt_http_chunk_buf_completion(nxt_task_t *task, void *obj, void *data) nxt_buf_t *b, *next, *parent; b = obj; - parent = data; nxt_debug(task, "buf completion: %p %p", b, b->mem.start); diff --git a/src/nxt_http_proxy.c b/src/nxt_http_proxy.c index 338d9fce..6aa3aabb 100644 --- a/src/nxt_http_proxy.c +++ b/src/nxt_http_proxy.c @@ -21,7 +21,7 @@ static void nxt_http_proxy_upstream_ready(nxt_task_t *task, nxt_upstream_server_t *us); static void nxt_http_proxy_upstream_error(nxt_task_t *task, nxt_upstream_server_t *us); -static nxt_http_action_t *nxt_http_proxy_handler(nxt_task_t *task, +static nxt_http_action_t *nxt_http_proxy(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action); static void nxt_http_proxy_header_send(nxt_task_t *task, void *obj, void *data); static void nxt_http_proxy_header_sent(nxt_task_t *task, void *obj, void *data); @@ -50,7 +50,8 @@ static const nxt_upstream_peer_state_t nxt_upstream_proxy_state = { nxt_int_t -nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action) +nxt_http_proxy_init(nxt_mp_t *mp, nxt_http_action_t *action, + nxt_http_action_conf_t *acf) { nxt_str_t name; nxt_sockaddr_t *sa; @@ -58,7 +59,7 @@ nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action) nxt_upstream_proxy_t *proxy; sa = NULL; - name = action->name; + nxt_conf_get_string(acf->proxy, &name); if (nxt_str_start(&name, "http://", 7)) { name.length -= 7; @@ -92,7 +93,7 @@ nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action) up->type.proxy = proxy; action->u.upstream = up; - action->handler = nxt_http_proxy_handler; + action->handler = nxt_http_proxy; } return NXT_OK; @@ -100,10 +101,16 @@ nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action) static nxt_http_action_t * -nxt_http_proxy_handler(nxt_task_t *task, nxt_http_request_t *r, +nxt_http_proxy(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action) { - return nxt_upstream_proxy_handler(task, r, action->u.upstream); + nxt_upstream_t *u; + + u = action->u.upstream; + + nxt_debug(task, "http proxy: \"%V\"", &u->name); + + return nxt_upstream_proxy_handler(task, r, u); } diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 779cfcf8..b71b25d9 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -10,6 +10,10 @@ static nxt_int_t nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp); static void nxt_http_request_start(nxt_task_t *task, void *obj, void *data); +static nxt_int_t nxt_http_request_client_ip(nxt_task_t *task, + nxt_http_request_t *r); +static nxt_sockaddr_t *nxt_http_request_client_ip_sockaddr( + nxt_http_request_t *r, u_char *start, size_t len); static void nxt_http_request_ready(nxt_task_t *task, void *obj, void *data); static void nxt_http_request_proto_info(nxt_task_t *task, nxt_http_request_t *r); @@ -272,16 +276,162 @@ static const nxt_http_request_state_t nxt_http_request_init_state static void nxt_http_request_start(nxt_task_t *task, void *obj, void *data) { + nxt_int_t ret; nxt_http_request_t *r; r = obj; r->state = &nxt_http_request_body_state; + ret = nxt_http_request_client_ip(task, r); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); + } + nxt_http_request_read_body(task, r); } +static nxt_int_t +nxt_http_request_client_ip(nxt_task_t *task, nxt_http_request_t *r) +{ + u_char *start, *p; + nxt_int_t ret, i, len; + nxt_str_t *header; + nxt_array_t *fields_arr; /* of nxt_http_field_t * */ + nxt_sockaddr_t *sa, *prev_sa; + nxt_http_field_t *f, **fields; + nxt_http_client_ip_t *client_ip; + + client_ip = r->conf->socket_conf->client_ip; + + if (client_ip == NULL) { + return NXT_OK; + } + + ret = nxt_http_route_addr_rule(r, client_ip->source, r->remote); + if (ret <= 0) { + return NXT_OK; + } + + header = client_ip->header; + + fields_arr = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_field_t *)); + if (nxt_slow_path(fields_arr == NULL)) { + return NXT_ERROR; + } + + nxt_list_each(f, r->fields) { + if (f->hash == client_ip->header_hash + && f->name_length == client_ip->header->length + && f->value_length > 0 + && nxt_memcasecmp(f->name, header->start, header->length) == 0) + { + fields = nxt_array_add(fields_arr); + if (nxt_slow_path(fields == NULL)) { + return NXT_ERROR; + } + + *fields = f; + } + } nxt_list_loop; + + prev_sa = r->remote; + fields = (nxt_http_field_t **) fields_arr->elts; + + i = fields_arr->nelts; + + while (i-- > 0) { + f = fields[i]; + start = f->value; + len = f->value_length; + + do { + for (p = start + len - 1; p > start; p--, len--) { + if (*p != ' ' && *p != ',') { + break; + } + } + + for (/* void */; p > start; p--) { + if (*p == ' ' || *p == ',') { + p++; + break; + } + } + + sa = nxt_http_request_client_ip_sockaddr(r, p, len - (p - start)); + if (nxt_slow_path(sa == NULL)) { + if (prev_sa != NULL) { + r->remote = prev_sa; + } + + return NXT_OK; + } + + if (!client_ip->recursive) { + r->remote = sa; + + return NXT_OK; + } + + ret = nxt_http_route_addr_rule(r, client_ip->source, sa); + if (ret <= 0 || (i == 0 && p == start)) { + r->remote = sa; + + return NXT_OK; + } + + prev_sa = sa; + len = p - 1 - start; + + } while (len > 0); + } + + return NXT_OK; +} + + +static nxt_sockaddr_t * +nxt_http_request_client_ip_sockaddr(nxt_http_request_t *r, u_char *start, + size_t len) +{ + nxt_str_t addr; + nxt_sockaddr_t *sa; + + addr.start = start; + addr.length = len; + + sa = nxt_sockaddr_parse_optport(r->mem_pool, &addr); + if (nxt_slow_path(sa == NULL)) { + return NULL; + } + + switch (sa->u.sockaddr.sa_family) { + case AF_INET: + if (sa->u.sockaddr_in.sin_addr.s_addr == INADDR_ANY) { + return NULL; + } + + break; + +#if (NXT_INET6) + case AF_INET6: + if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sockaddr_in6.sin6_addr)) { + return NULL; + } + + break; +#endif /* NXT_INET6 */ + + default: + return NULL; + } + + return sa; +} + + static const nxt_http_request_state_t nxt_http_request_body_state nxt_aligned(64) = { @@ -348,9 +498,7 @@ nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_str_set(&r->server_name, "localhost"); } - r->app_target = action->u.app.target; - - nxt_router_process_http_request(task, r, action->u.app.application); + nxt_router_process_http_request(task, r, action); return NULL; } diff --git a/src/nxt_http_return.c b/src/nxt_http_return.c index c466cc25..18fd490d 100644 --- a/src/nxt_http_return.c +++ b/src/nxt_http_return.c @@ -7,29 +7,90 @@ #include <nxt_http.h> +typedef struct { + nxt_http_status_t status; + nxt_str_t location; +} nxt_http_return_conf_t; + + +static nxt_http_action_t *nxt_http_return(nxt_task_t *task, + nxt_http_request_t *r, nxt_http_action_t *action); + + static const nxt_http_request_state_t nxt_http_return_send_state; +nxt_int_t +nxt_http_return_init(nxt_mp_t *mp, nxt_http_action_t *action, + nxt_http_action_conf_t *acf) +{ + nxt_str_t *loc; + nxt_uint_t encode; + nxt_http_return_conf_t *conf; + + conf = nxt_mp_zget(mp, sizeof(nxt_http_return_conf_t)); + if (nxt_slow_path(conf == NULL)) { + return NXT_ERROR; + } + + action->handler = nxt_http_return; + action->u.conf = conf; + + conf->status = nxt_conf_get_number(acf->ret); + + if (acf->location.length > 0) { + if (nxt_is_complex_uri_encoded(acf->location.start, + acf->location.length)) + { + loc = nxt_str_dup(mp, &conf->location, &acf->location); + if (nxt_slow_path(loc == NULL)) { + return NXT_ERROR; + } + + } else { + loc = &conf->location; + + encode = nxt_encode_complex_uri(NULL, acf->location.start, + acf->location.length); + loc->length = acf->location.length + encode * 2; + + loc->start = nxt_mp_nget(mp, loc->length); + if (nxt_slow_path(loc->start == NULL)) { + return NXT_ERROR; + } + + nxt_encode_complex_uri(loc->start, acf->location.start, + acf->location.length); + } + } + + return NXT_OK; +} + + nxt_http_action_t * -nxt_http_return_handler(nxt_task_t *task, nxt_http_request_t *r, +nxt_http_return(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action) { - nxt_http_field_t *field; - nxt_http_status_t status; + nxt_http_field_t *field; + nxt_http_return_conf_t *conf; + + conf = action->u.conf; - status = action->u.return_code; + nxt_debug(task, "http return: %d (loc: \"%V\")", + conf->status, &conf->location); - if (status >= NXT_HTTP_BAD_REQUEST - && status <= NXT_HTTP_SERVER_ERROR_MAX) + if (conf->status >= NXT_HTTP_BAD_REQUEST + && conf->status <= NXT_HTTP_SERVER_ERROR_MAX) { - nxt_http_request_error(task, r, status); + nxt_http_request_error(task, r, conf->status); return NULL; } - r->status = status; + r->status = conf->status; r->resp.content_length_n = 0; - if (action->name.length > 0) { + if (conf->location.length > 0) { field = nxt_list_zero_add(r->resp.fields); if (nxt_slow_path(field == NULL)) { nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); @@ -38,8 +99,8 @@ nxt_http_return_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_http_field_name_set(field, "Location"); - field->value = action->name.start; - field->value_length = action->name.length; + field->value = conf->location.start; + field->value_length = conf->location.length; } r->state = &nxt_http_return_send_state; diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 15b85544..cff69f96 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -47,20 +47,6 @@ typedef enum { typedef struct { - nxt_conf_value_t *pass; - nxt_conf_value_t *ret; - nxt_str_t location; - nxt_conf_value_t *proxy; - nxt_conf_value_t *share; - nxt_str_t chroot; - nxt_conf_value_t *follow_symlinks; - nxt_conf_value_t *traverse_mounts; - nxt_conf_value_t *types; - nxt_conf_value_t *fallback; -} nxt_http_route_action_conf_t; - - -typedef struct { nxt_conf_value_t *host; nxt_conf_value_t *uri; nxt_conf_value_t *method; @@ -149,12 +135,12 @@ typedef struct { } nxt_http_route_table_t; -typedef struct { +struct nxt_http_route_addr_rule_s { /* The object must be the first field. */ nxt_http_route_object_t object:8; uint32_t items; nxt_http_route_addr_pattern_t addr_pattern[0]; -} nxt_http_route_addr_rule_t; +}; typedef union { @@ -199,9 +185,6 @@ 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_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); @@ -211,8 +194,6 @@ static nxt_http_route_ruleset_t *nxt_http_route_ruleset_create(nxt_task_t *task, static nxt_http_route_rule_t *nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding); -static nxt_http_route_addr_rule_t *nxt_http_route_addr_rule_create( - nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv); static nxt_http_route_rule_t *nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive, nxt_http_route_pattern_case_t pattern_case, @@ -254,8 +235,6 @@ static nxt_int_t nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table); static nxt_int_t nxt_http_route_ruleset(nxt_http_request_t *r, nxt_http_route_ruleset_t *ruleset); -static nxt_int_t nxt_http_route_addr_rule(nxt_http_request_t *r, - nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sockaddr); static nxt_int_t nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule); static nxt_int_t nxt_http_route_header(nxt_http_request_t *r, @@ -476,7 +455,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NULL; } - ret = nxt_http_route_action_create(task, tmcf, action_conf, &match->action); + ret = nxt_http_action_init(task, tmcf, action_conf, &match->action); if (nxt_slow_path(ret != NXT_OK)) { return NULL; } @@ -617,77 +596,69 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = { { nxt_string("pass"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, pass) + offsetof(nxt_http_action_conf_t, pass) }, { nxt_string("return"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, ret) + offsetof(nxt_http_action_conf_t, ret) }, { nxt_string("location"), NXT_CONF_MAP_STR, - offsetof(nxt_http_route_action_conf_t, location) + offsetof(nxt_http_action_conf_t, location) }, { nxt_string("proxy"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, proxy) + offsetof(nxt_http_action_conf_t, proxy) }, { nxt_string("share"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, share) + offsetof(nxt_http_action_conf_t, share) }, { nxt_string("chroot"), NXT_CONF_MAP_STR, - offsetof(nxt_http_route_action_conf_t, chroot) + offsetof(nxt_http_action_conf_t, chroot) }, { nxt_string("follow_symlinks"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, follow_symlinks) + offsetof(nxt_http_action_conf_t, follow_symlinks) }, { nxt_string("traverse_mounts"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, traverse_mounts) + offsetof(nxt_http_action_conf_t, traverse_mounts) }, { nxt_string("types"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, types) + offsetof(nxt_http_action_conf_t, types) }, { nxt_string("fallback"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, fallback) + offsetof(nxt_http_action_conf_t, fallback) }, }; -static nxt_int_t -nxt_http_route_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, +nxt_int_t +nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, nxt_http_action_t *action) { -#if (NXT_HAVE_OPENAT2) - u_char *p; - uint8_t slash; - nxt_str_t *chroot; -#endif - nxt_mp_t *mp; - nxt_int_t ret; - nxt_str_t name, *string; - nxt_uint_t encode; - nxt_conf_value_t *conf; - nxt_http_route_rule_t *rule; - nxt_http_route_action_conf_t accf; + nxt_mp_t *mp; + nxt_int_t ret; + nxt_str_t name, *string; + nxt_http_action_conf_t acf; - nxt_memzero(&accf, sizeof(accf)); + nxt_memzero(&acf, sizeof(acf)); ret = nxt_conf_map_object(tmcf->mem_pool, cv, nxt_http_route_action_conf, - nxt_nitems(nxt_http_route_action_conf), &accf); + nxt_nitems(nxt_http_route_action_conf), &acf); if (ret != NXT_OK) { return ret; } @@ -696,126 +667,25 @@ nxt_http_route_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, mp = tmcf->router_conf->mem_pool; - if (accf.ret != NULL) { - action->handler = nxt_http_return_handler; - action->u.return_code = nxt_conf_get_number(accf.ret); - - if (accf.location.length > 0) { - if (nxt_is_complex_uri_encoded(accf.location.start, - accf.location.length)) - { - string = nxt_str_dup(mp, &action->name, &accf.location); - if (nxt_slow_path(string == NULL)) { - return NXT_ERROR; - } - - } else { - string = &action->name; - - encode = nxt_encode_complex_uri(NULL, accf.location.start, - accf.location.length); - string->length = accf.location.length + encode * 2; - - string->start = nxt_mp_nget(mp, string->length); - if (nxt_slow_path(string->start == NULL)) { - return NXT_ERROR; - } - - nxt_encode_complex_uri(string->start, accf.location.start, - accf.location.length); - } - } - - return NXT_OK; + if (acf.ret != NULL) { + return nxt_http_return_init(mp, action, &acf); } - if (accf.share != NULL) { - conf = accf.share; - - } else if (accf.proxy != NULL) { - conf = accf.proxy; + if (acf.share != NULL) { + return nxt_http_static_init(task, tmcf, action, &acf); + } - } else { - conf = accf.pass; + if (acf.proxy != NULL) { + return nxt_http_proxy_init(mp, action, &acf); } - nxt_conf_get_string(conf, &name); + nxt_conf_get_string(acf.pass, &name); string = nxt_str_dup(mp, &action->name, &name); if (nxt_slow_path(string == NULL)) { return NXT_ERROR; } - if (accf.share != NULL) { - action->handler = nxt_http_static_handler; - -#if (NXT_HAVE_OPENAT2) - string = &accf.chroot; - chroot = &action->u.share.chroot; - - if (string->length > 0) { - action->u.share.resolve |= RESOLVE_IN_ROOT; - - slash = (string->start[string->length - 1] != '/'); - - chroot->length = string->length + (slash ? 1 : 0); - - chroot->start = nxt_mp_alloc(mp, chroot->length + 1); - if (nxt_slow_path(chroot->start == NULL)) { - return NXT_ERROR; - } - - p = nxt_cpymem(chroot->start, string->start, string->length); - - if (slash) { - *p++ = '/'; - } - - *p = '\0'; - } - - if (accf.follow_symlinks != NULL - && !nxt_conf_get_boolean(accf.follow_symlinks)) - { - action->u.share.resolve |= RESOLVE_NO_SYMLINKS; - } - - if (accf.traverse_mounts != NULL - && !nxt_conf_get_boolean(accf.traverse_mounts)) - { - action->u.share.resolve |= RESOLVE_NO_XDEV; - } -#endif - - if (accf.types != NULL) { - rule = nxt_http_route_rule_create(task, mp, accf.types, 0, - NXT_HTTP_ROUTE_PATTERN_LOWCASE, - NXT_HTTP_ROUTE_ENCODING_NONE); - if (nxt_slow_path(rule == NULL)) { - return NXT_ERROR; - } - - action->u.share.types = rule; - } - - if (accf.fallback != NULL) { - action->u.share.fallback = nxt_mp_alloc(mp, - sizeof(nxt_http_action_t)); - if (nxt_slow_path(action->u.share.fallback == NULL)) { - return NXT_ERROR; - } - - return nxt_http_route_action_create(task, tmcf, accf.fallback, - action->u.share.fallback); - } - - return NXT_OK; - } - - if (accf.proxy != NULL) { - return nxt_http_proxy_create(mp, action); - } - return NXT_OK; } @@ -1066,7 +936,7 @@ nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp, } -static nxt_http_route_addr_rule_t * +nxt_http_route_addr_rule_t * nxt_http_route_addr_rule_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv) { @@ -1119,6 +989,16 @@ nxt_http_route_addr_rule_create(nxt_task_t *task, nxt_mp_t *mp, } +nxt_http_route_rule_t * +nxt_http_route_types_rule_create(nxt_task_t *task, nxt_mp_t *mp, + nxt_conf_value_t *types) +{ + return nxt_http_route_rule_create(task, mp, types, 0, + NXT_HTTP_ROUTE_PATTERN_LOWCASE, + NXT_HTTP_ROUTE_ENCODING_NONE); +} + + static int nxt_http_pattern_compare(const void *one, const void *two) { @@ -1491,15 +1371,12 @@ static nxt_int_t nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action) { - nxt_var_t *var; nxt_int_t ret; + nxt_var_t *var; if (action->handler != NULL) { - if (action->handler == nxt_http_static_handler - && action->u.share.fallback != NULL) - { - return nxt_http_action_resolve(task, tmcf, - action->u.share.fallback); + if (action->fallback != NULL) { + return nxt_http_action_resolve(task, tmcf, action->fallback); } return NXT_OK; @@ -1601,9 +1478,7 @@ static nxt_int_t nxt_http_pass_find(nxt_task_t *task, nxt_mp_t *mp, nxt_router_conf_t *rtcf, nxt_http_action_t *action) { - nxt_str_t *targets; nxt_int_t ret; - nxt_uint_t i; nxt_str_t segments[3]; ret = nxt_http_pass_segments(mp, &action->name, segments, 3); @@ -1612,24 +1487,8 @@ nxt_http_pass_find(nxt_task_t *task, nxt_mp_t *mp, nxt_router_conf_t *rtcf, } if (nxt_str_eq(&segments[0], "applications", 12)) { - ret = nxt_router_listener_application(rtcf, &segments[1], action); - - if (ret != NXT_OK) { - return ret; - } - - if (segments[2].length != 0) { - targets = action->u.app.application->targets; - - for (i = 0; !nxt_strstr_eq(&segments[2], &targets[i]); i++); - - action->u.app.target = i; - - } else { - action->u.app.target = 0; - } - - return NXT_OK; + return nxt_router_application_init(rtcf, &segments[1], &segments[2], + action); } if (segments[2].length == 0) { @@ -1704,6 +1563,10 @@ nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name, { nxt_http_route_t **route, **end; + if (routes == NULL) { + return NXT_DECLINED; + } + route = &routes->route[0]; end = route + routes->items; @@ -1762,9 +1625,7 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf, action->name = *name; - (void) nxt_router_listener_application(rtcf, name, action); - - action->u.app.target = 0; + (void) nxt_router_application_init(rtcf, name, NULL, action); return action; } @@ -2062,7 +1923,7 @@ nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p, } -static nxt_int_t +nxt_int_t nxt_http_route_addr_rule(nxt_http_request_t *r, nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sa) { @@ -2071,6 +1932,11 @@ nxt_http_route_addr_rule(nxt_http_request_t *r, nxt_http_route_addr_pattern_t *p; n = addr_rule->items; + + if (n == 0) { + return 0; + } + p = &addr_rule->addr_pattern[0] - 1; do { diff --git a/src/nxt_http_route_addr.c b/src/nxt_http_route_addr.c index 6d4955ed..2907a902 100644 --- a/src/nxt_http_route_addr.c +++ b/src/nxt_http_route_addr.c @@ -8,7 +8,6 @@ #include <nxt_http_route_addr.h> -static nxt_bool_t nxt_str_looks_like_ipv6(const nxt_str_t *str); #if (NXT_INET6) static nxt_bool_t nxt_valid_ipv6_blocks(u_char *c, size_t len); #endif @@ -57,7 +56,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp, goto parse_port; } - if (nxt_str_looks_like_ipv6(&addr)) { + if (nxt_inet6_probe(&addr)) { #if (NXT_INET6) u_char *end; uint8_t i; @@ -304,22 +303,6 @@ parse_port: } -static nxt_bool_t -nxt_str_looks_like_ipv6(const nxt_str_t *str) -{ - u_char *colon, *end; - - colon = nxt_memchr(str->start, ':', str->length); - - if (colon != NULL) { - end = str->start + str->length; - colon = nxt_memchr(colon + 1, ':', end - (colon + 1)); - } - - return (colon != NULL); -} - - #if (NXT_INET6) static nxt_bool_t diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index c8b73fac..9b79a666 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -7,12 +7,22 @@ #include <nxt_http.h> +typedef struct { + nxt_str_t share; + nxt_str_t chroot; + nxt_uint_t resolve; + nxt_http_route_rule_t *types; +} nxt_http_static_conf_t; + + #define NXT_HTTP_STATIC_BUF_COUNT 2 #define NXT_HTTP_STATIC_BUF_SIZE (128 * 1024) +static nxt_http_action_t *nxt_http_static(nxt_task_t *task, + nxt_http_request_t *r, nxt_http_action_t *action); static void nxt_http_static_extract_extension(nxt_str_t *path, - nxt_str_t *extension); + nxt_str_t *exten); static void nxt_http_static_body_handler(nxt_task_t *task, void *obj, void *data); static void nxt_http_static_buf_completion(nxt_task_t *task, void *obj, @@ -27,30 +37,122 @@ static void nxt_http_static_mtypes_hash_free(void *data, void *p); static const nxt_http_request_state_t nxt_http_static_send_state; -nxt_http_action_t * -nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, +nxt_int_t +nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_http_action_t *action, nxt_http_action_conf_t *acf) +{ + nxt_mp_t *mp; + nxt_str_t *str, value; + nxt_http_static_conf_t *conf; + + mp = tmcf->router_conf->mem_pool; + + conf = nxt_mp_zget(mp, sizeof(nxt_http_static_conf_t)); + if (nxt_slow_path(conf == NULL)) { + return NXT_ERROR; + } + + action->handler = nxt_http_static; + action->u.conf = conf; + + nxt_conf_get_string(acf->share, &value); + + str = nxt_str_dup(mp, &conf->share, &value); + if (nxt_slow_path(str == NULL)) { + return NXT_ERROR; + } + +#if (NXT_HAVE_OPENAT2) + if (acf->chroot.length > 0) { + u_char *p; + nxt_str_t slash; + + if (acf->chroot.start[acf->chroot.length - 1] != '/') { + nxt_str_set(&slash, "/"); + + } else { + nxt_str_set(&slash, ""); + } + + value.length = acf->chroot.length + slash.length; + + value.start = nxt_mp_alloc(mp, value.length + 1); + if (nxt_slow_path(value.start == NULL)) { + return NXT_ERROR; + } + + p = value.start; + p = nxt_cpymem(p, acf->chroot.start, acf->chroot.length); + p = nxt_cpymem(p, slash.start, slash.length); + *p = '\0'; + + conf->chroot = value; + conf->resolve |= RESOLVE_IN_ROOT; + } + + if (acf->follow_symlinks != NULL + && !nxt_conf_get_boolean(acf->follow_symlinks)) + { + conf->resolve |= RESOLVE_NO_SYMLINKS; + } + + if (acf->traverse_mounts != NULL + && !nxt_conf_get_boolean(acf->traverse_mounts)) + { + conf->resolve |= RESOLVE_NO_XDEV; + } +#endif + + if (acf->types != NULL) { + conf->types = nxt_http_route_types_rule_create(task, mp, acf->types); + if (nxt_slow_path(conf->types == NULL)) { + return NXT_ERROR; + } + } + + if (acf->fallback != NULL) { + action->fallback = nxt_mp_alloc(mp, sizeof(nxt_http_action_t)); + if (nxt_slow_path(action->fallback == NULL)) { + return NXT_ERROR; + } + + return nxt_http_action_init(task, tmcf, acf->fallback, + action->fallback); + } + + return NXT_OK; +} + + +static nxt_http_action_t * +nxt_http_static(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action) { - 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, *chroot; - nxt_uint_t level; - nxt_bool_t need_body; - nxt_file_t *f, file; - nxt_file_info_t fi; - nxt_http_field_t *field; - nxt_http_status_t status; - nxt_router_conf_t *rtcf; - nxt_work_handler_t body_handler; + size_t length, encode; + u_char *p, *fname; + struct tm tm; + nxt_buf_t *fb; + nxt_int_t ret; + nxt_str_t index, exten, *mtype, *chroot; + nxt_uint_t level; + nxt_bool_t need_body; + nxt_file_t *f, file; + nxt_file_info_t fi; + nxt_http_field_t *field; + nxt_http_status_t status; + nxt_router_conf_t *rtcf; + nxt_work_handler_t body_handler; + nxt_http_static_conf_t *conf; + + conf = action->u.conf; + + nxt_debug(task, "http static: \"%V\"", &conf->share); if (nxt_slow_path(!nxt_str_eq(r->method, "GET", 3))) { if (!nxt_str_eq(r->method, "HEAD", 4)) { - if (action->u.share.fallback != NULL) { - return action->u.share.fallback; + if (action->fallback != NULL) { + return action->fallback; } nxt_http_request_error(task, r, NXT_HTTP_METHOD_NOT_ALLOWED); @@ -66,11 +168,11 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, if (r->path->start[r->path->length - 1] == '/') { /* TODO: dynamic index setting. */ nxt_str_set(&index, "index.html"); - nxt_str_set(&extension, ".html"); + nxt_str_set(&exten, ".html"); } else { nxt_str_set(&index, ""); - nxt_str_null(&extension); + nxt_str_null(&exten); } f = NULL; @@ -79,20 +181,19 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, 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 (conf->types != NULL && exten.start == NULL) { + nxt_http_static_extract_extension(r->path, &exten); + mtype = nxt_http_static_mtype_get(&rtcf->mtypes_hash, &exten); - ret = nxt_http_route_test_rule(r, action->u.share.types, - mtype->start, mtype->length); + ret = nxt_http_route_test_rule(r, conf->types, mtype->start, + mtype->length); if (nxt_slow_path(ret == NXT_ERROR)) { goto fail; } if (ret == 0) { - if (action->u.share.fallback != NULL) { - return action->u.share.fallback; + if (action->fallback != NULL) { + return action->fallback; } nxt_http_request_error(task, r, NXT_HTTP_FORBIDDEN); @@ -100,7 +201,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, } } - length = action->name.length + r->path->length + index.length; + length = conf->share.length + r->path->length + index.length; fname = nxt_mp_nget(r->mem_pool, length + 1); if (nxt_slow_path(fname == NULL)) { @@ -108,7 +209,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, } p = fname; - p = nxt_cpymem(p, action->name.start, action->name.length); + p = nxt_cpymem(p, conf->share.start, conf->share.length); p = nxt_cpymem(p, r->path->start, r->path->length); p = nxt_cpymem(p, index.start, index.length); *p = '\0'; @@ -117,11 +218,10 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, file.name = fname; - chroot = &action->u.share.chroot; + chroot = &conf->chroot; #if (NXT_HAVE_OPENAT2) - - if (action->u.share.resolve != 0) { + if (conf->resolve != 0) { if (chroot->length > 0) { file.name = chroot->start; @@ -156,8 +256,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, file.name = fname; ret = nxt_file_openat2(task, &file, NXT_FILE_RDONLY, - NXT_FILE_OPEN, 0, af.fd, - action->u.share.resolve); + NXT_FILE_OPEN, 0, af.fd, conf->resolve); if (af.fd != AT_FDCWD) { nxt_file_close(task, &af); @@ -169,9 +268,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, } #else - ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0); - #endif if (nxt_slow_path(ret != NXT_OK)) { @@ -211,8 +308,8 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, break; } - if (level == NXT_LOG_ERR && action->u.share.fallback != NULL) { - return action->u.share.fallback; + if (level == NXT_LOG_ERR && action->fallback != NULL) { + return action->fallback; } if (status != NXT_HTTP_NOT_FOUND) { @@ -283,13 +380,12 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_file_size(&fi)) - p; - if (extension.start == NULL) { - nxt_http_static_extract_extension(r->path, &extension); + if (exten.start == NULL) { + nxt_http_static_extract_extension(r->path, &exten); } if (mtype == NULL) { - mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash, - &extension); + mtype = nxt_http_static_mtype_get(&rtcf->mtypes_hash, &exten); } if (mtype->length != 0) { @@ -328,8 +424,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.share.fallback != NULL) { - return action->u.share.fallback; + if (action->fallback != NULL) { + return action->fallback; } nxt_log(task, NXT_LOG_ERR, "\"%FN\" is not a regular file", @@ -401,7 +497,7 @@ fail: static void -nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *extension) +nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *exten) { u_char ch, *p, *end; @@ -419,8 +515,8 @@ nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *extension) p++; /* Fall through. */ case '.': - extension->length = end - p; - extension->start = p; + exten->length = end - p; + exten->start = p; return; } } @@ -571,13 +667,13 @@ clean: nxt_int_t nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash) { - nxt_str_t *type, extension; + nxt_str_t *type, exten; nxt_int_t ret; nxt_uint_t i; static const struct { nxt_str_t type; - const char *extension; + const char *exten; } default_types[] = { { nxt_string("text/html"), ".html" }, @@ -644,10 +740,10 @@ nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash) for (i = 0; i < nxt_nitems(default_types); i++) { type = (nxt_str_t *) &default_types[i].type; - extension.start = (u_char *) default_types[i].extension; - extension.length = nxt_strlen(extension.start); + exten.start = (u_char *) default_types[i].exten; + exten.length = nxt_strlen(exten.start); - ret = nxt_http_static_mtypes_hash_add(mp, hash, &extension, type); + ret = nxt_http_static_mtypes_hash_add(mp, hash, &exten, type); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } @@ -668,14 +764,14 @@ static const nxt_lvlhsh_proto_t nxt_http_static_mtypes_hash_proto typedef struct { - nxt_str_t extension; + nxt_str_t exten; nxt_str_t *type; } nxt_http_static_mtype_t; nxt_int_t nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash, - nxt_str_t *extension, nxt_str_t *type) + nxt_str_t *exten, nxt_str_t *type) { nxt_lvlhsh_query_t lhq; nxt_http_static_mtype_t *mtype; @@ -685,10 +781,10 @@ nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash, return NXT_ERROR; } - mtype->extension = *extension; + mtype->exten = *exten; mtype->type = type; - lhq.key = *extension; + lhq.key = *exten; lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length); lhq.replace = 1; lhq.value = mtype; @@ -700,14 +796,14 @@ nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash, nxt_str_t * -nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash, nxt_str_t *extension) +nxt_http_static_mtype_get(nxt_lvlhsh_t *hash, nxt_str_t *exten) { nxt_lvlhsh_query_t lhq; nxt_http_static_mtype_t *mtype; static nxt_str_t empty = nxt_string(""); - lhq.key = *extension; + lhq.key = *exten; lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length); lhq.proto = &nxt_http_static_mtypes_hash_proto; @@ -727,8 +823,7 @@ nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t *lhq, void *data) mtype = data; - return nxt_strcasestr_eq(&lhq->key, &mtype->extension) ? NXT_OK - : NXT_DECLINED; + return nxt_strcasestr_eq(&lhq->key, &mtype->exten) ? NXT_OK : NXT_DECLINED; } diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c index cab0074b..e3cb1f22 100644 --- a/src/nxt_isolation.c +++ b/src/nxt_isolation.c @@ -126,10 +126,10 @@ nxt_isolation_main_prefork(nxt_task_t *task, nxt_process_t *process, return ret; } - has_mnt = 0; - #if (NXT_HAVE_CLONE_NEWNS) has_mnt = nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS); +#else + has_mnt = 0; #endif if (process->user_cred->uid == 0 && !has_mnt) { diff --git a/src/nxt_log.h b/src/nxt_log.h index 48742721..0cf10b5c 100644 --- a/src/nxt_log.h +++ b/src/nxt_log.h @@ -48,29 +48,29 @@ nxt_log_level_enough(log, level) \ #define nxt_alert(task, ...) \ do { \ - nxt_log_t *log = (task)->log; \ + nxt_log_t *_log = (task)->log; \ \ - log->handler(NXT_LOG_ALERT, log, __VA_ARGS__); \ + _log->handler(NXT_LOG_ALERT, _log, __VA_ARGS__); \ } while (0) #define nxt_log(task, _level, ...) \ do { \ - nxt_log_t *log = (task)->log; \ + nxt_log_t *_log = (task)->log; \ nxt_uint_t _level_ = (_level); \ \ - if (nxt_slow_path(log->level >= _level_)) { \ - log->handler(_level_, log, __VA_ARGS__); \ + if (nxt_slow_path(_log->level >= _level_)) { \ + _log->handler(_level_, _log, __VA_ARGS__); \ } \ } while (0) #define nxt_trace(task, ...) \ do { \ - nxt_log_t *log = (task)->log; \ + nxt_log_t *_log = (task)->log; \ \ - if (nxt_slow_path(log->level >= NXT_LOG_NOTICE || nxt_trace)) { \ - log->handler(NXT_LOG_NOTICE, log, __VA_ARGS__); \ + if (nxt_slow_path(_log->level >= NXT_LOG_NOTICE || nxt_trace)) { \ + _log->handler(NXT_LOG_NOTICE, _log, __VA_ARGS__); \ } \ } while (0) @@ -99,10 +99,10 @@ nxt_log_error(_level, _log, ...) \ #define nxt_debug(task, ...) \ do { \ - nxt_log_t *log = (task)->log; \ + nxt_log_t *_log = (task)->log; \ \ - if (nxt_slow_path(log->level == NXT_LOG_DEBUG || nxt_debug)) { \ - log->handler(NXT_LOG_DEBUG, log, __VA_ARGS__); \ + if (nxt_slow_path(_log->level == NXT_LOG_DEBUG || nxt_debug)) { \ + _log->handler(NXT_LOG_DEBUG, _log, __VA_ARGS__); \ } \ } while (0) diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 00f336f6..16c6a297 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -271,6 +271,11 @@ static nxt_conf_map_t nxt_ruby_app_conf[] = { NXT_CONF_MAP_INT32, offsetof(nxt_common_app_conf_t, u.ruby.threads), }, + { + nxt_string("hooks"), + NXT_CONF_MAP_STR, + offsetof(nxt_common_app_conf_t, u.ruby.hooks), + } }; @@ -342,8 +347,6 @@ nxt_port_main_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_process_init_t *init; nxt_common_app_conf_t *app_conf; - ret = NXT_ERROR; - rt = task->thread->runtime; process = nxt_main_process_new(task, rt); diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index 2fd5d1bf..273ca7f4 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -11,6 +11,8 @@ #include <openssl/err.h> #include <openssl/rand.h> #include <openssl/x509v3.h> +#include <openssl/bio.h> +#include <openssl/evp.h> typedef struct { @@ -42,15 +44,22 @@ static void nxt_openssl_lock(int mode, int type, const char *file, int line); 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_conf_value_t *conf_cmds, - nxt_bool_t last); +static nxt_int_t nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp, + nxt_tls_init_t *tls_init, 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 +#if (NXT_HAVE_OPENSSL_TLSEXT) +static nxt_int_t nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, + nxt_tls_init_t *tls_init, nxt_mp_t *mp); +static int nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, + unsigned char *iv, EVP_CIPHER_CTX *ectx,HMAC_CTX *hctx, int enc); +#endif +static void nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size, + time_t timeout); 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, @@ -265,11 +274,12 @@ 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_conf_value_t *conf_cmds, nxt_bool_t last) +nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp, + nxt_tls_init_t *tls_init, nxt_bool_t last) { SSL_CTX *ctx; const char *ciphers, *ca_certificate; + nxt_tls_conf_t *conf; STACK_OF(X509_NAME) *list; nxt_tls_bundle_conf_t *bundle; @@ -279,6 +289,8 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf, return NXT_ERROR; } + conf = tls_init->conf; + bundle = conf->bundle; nxt_assert(bundle != NULL); @@ -337,13 +349,21 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf, } #if (NXT_HAVE_OPENSSL_CONF_CMD) - if (conf_cmds != NULL - && nxt_ssl_conf_commands(task, ctx, conf_cmds, mp) != NXT_OK) + if (tls_init->conf_cmds != NULL + && nxt_ssl_conf_commands(task, ctx, tls_init->conf_cmds, mp) != NXT_OK) { goto fail; } #endif + nxt_ssl_session_cache(ctx, tls_init->cache_size, tls_init->timeout); + +#if (NXT_HAVE_OPENSSL_TLSEXT) + if (nxt_tls_ticket_keys(task, ctx, tls_init, mp) != NXT_OK) { + goto fail; + } +#endif + SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); if (conf->ca_certificate != NULL) { @@ -581,6 +601,257 @@ fail: #endif +#if (NXT_HAVE_OPENSSL_TLSEXT) + +static nxt_int_t +nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_init_t *tls_init, + nxt_mp_t *mp) +{ + uint32_t i; + nxt_int_t ret; + nxt_str_t value; + nxt_uint_t count; + nxt_conf_value_t *member, *tickets_conf; + nxt_tls_ticket_t *ticket; + nxt_tls_tickets_t *tickets; + u_char buf[80]; + + tickets_conf = tls_init->tickets_conf; + + if (tickets_conf == NULL) { + goto no_ticket; + } + + if (nxt_conf_type(tickets_conf) == NXT_CONF_BOOLEAN) { + if (nxt_conf_get_boolean(tickets_conf) == 0) { + goto no_ticket; + } + + return NXT_OK; + } + + if (nxt_conf_type(tickets_conf) == NXT_CONF_ARRAY) { + count = nxt_conf_array_elements_count(tickets_conf); + + if (count == 0) { + goto no_ticket; + } + + } else { + /* nxt_conf_type(tickets_conf) == NXT_CONF_STRING */ + count = 1; + } + +#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB + + tickets = nxt_mp_get(mp, sizeof(nxt_tls_tickets_t) + + count * sizeof(nxt_tls_ticket_t)); + if (nxt_slow_path(tickets == NULL)) { + return NXT_ERROR; + } + + tickets->count = count; + tls_init->conf->tickets = tickets; + i = 0; + + do { + ticket = &tickets->tickets[i]; + + i++; + + if (nxt_conf_type(tickets_conf) == NXT_CONF_ARRAY) { + member = nxt_conf_get_array_element(tickets_conf, count - i); + if (member == NULL) { + break; + } + + } else { + /* nxt_conf_type(tickets_conf) == NXT_CONF_STRING */ + member = tickets_conf; + } + + nxt_conf_get_string(member, &value); + + ret = nxt_openssl_base64_decode(buf, 80, value.start, value.length); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } + + if (ret == 48) { + ticket->aes128 = 1; + nxt_memcpy(ticket->aes_key, buf + 16, 16); + nxt_memcpy(ticket->hmac_key, buf + 32, 16); + + } else { + ticket->aes128 = 0; + nxt_memcpy(ticket->hmac_key, buf + 16, 32); + nxt_memcpy(ticket->aes_key, buf + 48, 32); + } + + nxt_memcpy(ticket->name, buf, 16); + } while (i < count); + + if (SSL_CTX_set_tlsext_ticket_key_cb(ctx, nxt_tls_ticket_key_callback) + == 0) + { + nxt_openssl_log_error(task, NXT_LOG_ALERT, + "Unit was built with Session Tickets support, however, " + "now it is linked dynamically to an OpenSSL library " + "which has no tlsext support, therefore Session Tickets " + "are not available"); + + return NXT_ERROR; + } + + return NXT_OK; + +#else + nxt_alert(task, "Setting custom session ticket keys is not supported with " + "this version of OpenSSL library"); + + return NXT_ERROR; + +#endif + +no_ticket: + + SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET); + + return NXT_OK; +} + + +#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB + +static int +nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, unsigned char *iv, + EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc) +{ + size_t size; + nxt_uint_t i; + nxt_conn_t *c; + const EVP_MD *digest; + const EVP_CIPHER *cipher; + nxt_tls_ticket_t *ticket; + nxt_openssl_conn_t *tls; + + c = SSL_get_ex_data(s, nxt_openssl_connection_index); + + if (nxt_slow_path(c == NULL)) { + nxt_thread_log_alert("SSL_get_ex_data() failed"); + return -1; + } + + tls = c->u.tls; + ticket = tls->conf->tickets->tickets; + +#ifdef OPENSSL_NO_SHA256 + digest = EVP_sha1(); +#else + digest = EVP_sha256(); +#endif + + if (enc == 1) { + /* encrypt session ticket */ + + nxt_debug(c->socket.task, "TLS session ticket encrypt"); + + if (ticket[0].aes128 == 1) { + cipher = EVP_aes_128_cbc(); + size = 16; + + } else { + cipher = EVP_aes_256_cbc(); + size = 32; + } + + if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) { + nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, + "RAND_bytes() failed"); + return -1; + } + + if (EVP_EncryptInit_ex(ectx, cipher, NULL, ticket[0].aes_key, iv) + != 1) + { + nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, + "EVP_EncryptInit_ex() failed"); + return -1; + } + + if (HMAC_Init_ex(hctx, ticket[0].hmac_key, size, digest, NULL) != 1) { + nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, + "HMAC_Init_ex() failed"); + return -1; + } + + nxt_memcpy(name, ticket[0].name, 16); + + return 1; + + } else { + /* decrypt session ticket */ + + for (i = 0; i < tls->conf->tickets->count; i++) { + if (nxt_memcmp(name, ticket[i].name, 16) == 0) { + goto found; + } + } + + nxt_debug(c->socket.task, "TLS session ticket decrypt, key not found"); + + return 0; + + found: + + nxt_debug(c->socket.task, + "TLS session ticket decrypt, key number: \"%d\"", i); + + if (ticket[i].aes128 == 1) { + cipher = EVP_aes_128_cbc(); + size = 16; + + } else { + cipher = EVP_aes_256_cbc(); + size = 32; + } + + if (EVP_DecryptInit_ex(ectx, cipher, NULL, ticket[i].aes_key, iv) != 1) { + nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, + "EVP_DecryptInit_ex() failed"); + return -1; + } + + if (HMAC_Init_ex(hctx, ticket[i].hmac_key, size, digest, NULL) != 1) { + nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, + "HMAC_Init_ex() failed"); + return -1; + } + + return (i == 0) ? 1 : 2 /* renew */; + } +} + +#endif /* SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB */ + +#endif /* NXT_HAVE_OPENSSL_TLSEXT */ + + +static void +nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size, time_t timeout) +{ + if (cache_size == 0) { + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); + return; + } + + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER); + + SSL_CTX_sess_set_cache_size(ctx, cache_size); + + SSL_CTX_set_timeout(ctx, (long) timeout); +} + static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf, @@ -782,15 +1053,15 @@ nxt_openssl_servername(SSL *s, int *ad, void *arg) } servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); - if (nxt_slow_path(servername == NULL)) { - nxt_log(c->socket.task, NXT_LOG_ALERT, "SSL_get_servername() returned " - "NULL in server name callback"); - return SSL_TLSEXT_ERR_ALERT_FATAL; + + if (servername == NULL) { + nxt_debug(c->socket.task, "SSL_get_servername(): NULL"); + goto done; } str.length = nxt_strlen(servername); if (str.length == 0) { - nxt_debug(c->socket.task, "client sent zero-length server name"); + nxt_debug(c->socket.task, "SSL_get_servername(): \"\" is empty"); goto done; } @@ -882,6 +1153,11 @@ nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf) bundle = bundle->next; } while (bundle != NULL); + if (conf->tickets) { + nxt_memzero(conf->tickets->tickets, + conf->tickets->count * sizeof(nxt_tls_ticket_t)); + } + #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \ && OPENSSL_VERSION_NUMBER < 0x1010101fL) RAND_keep_random_devices_open(0); @@ -1543,3 +1819,70 @@ nxt_openssl_copy_error(u_char *p, u_char *end) return p; } + + +nxt_int_t +nxt_openssl_base64_decode(u_char *d, size_t dlen, const u_char *s, size_t slen) +{ + BIO *bio, *b64; + nxt_int_t count, ret; + u_char buf[128]; + + b64 = BIO_new(BIO_f_base64()); + if (nxt_slow_path(b64 == NULL)) { + goto error; + } + + bio = BIO_new_mem_buf(s, slen); + if (nxt_slow_path(bio == NULL)) { + goto error; + } + + bio = BIO_push(b64, bio); + + BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); + + count = 0; + + if (d == NULL) { + + for ( ;; ) { + ret = BIO_read(bio, buf, 128); + + if (ret < 0) { + goto invalid; + } + + count += ret; + + if (ret != 128) { + break; + } + } + + } else { + count = BIO_read(bio, d, dlen); + + if (count < 0) { + goto invalid; + } + } + + BIO_free_all(bio); + + return count; + +error: + + BIO_vfree(b64); + ERR_clear_error(); + + return NXT_ERROR; + +invalid: + + BIO_free_all(bio); + ERR_clear_error(); + + return NXT_DECLINED; +} diff --git a/src/nxt_port.h b/src/nxt_port.h index 5ece3bfa..a0bc2512 100644 --- a/src/nxt_port.h +++ b/src/nxt_port.h @@ -50,6 +50,7 @@ struct nxt_port_handlers_s { /* Various data. */ nxt_port_handler_t data; + nxt_port_handler_t app_restart; nxt_port_handler_t oosm; nxt_port_handler_t shm_ack; @@ -100,6 +101,7 @@ typedef enum { _NXT_PORT_MSG_WEBSOCKET = nxt_port_handler_idx(websocket_frame), _NXT_PORT_MSG_DATA = nxt_port_handler_idx(data), + _NXT_PORT_MSG_APP_RESTART = nxt_port_handler_idx(app_restart), _NXT_PORT_MSG_OOSM = nxt_port_handler_idx(oosm), _NXT_PORT_MSG_SHM_ACK = nxt_port_handler_idx(shm_ack), @@ -139,6 +141,7 @@ typedef enum { NXT_PORT_MSG_DATA = _NXT_PORT_MSG_DATA, NXT_PORT_MSG_DATA_LAST = nxt_msg_last(_NXT_PORT_MSG_DATA), + NXT_PORT_MSG_APP_RESTART = nxt_msg_last(_NXT_PORT_MSG_APP_RESTART), NXT_PORT_MSG_OOSM = nxt_msg_last(_NXT_PORT_MSG_OOSM), NXT_PORT_MSG_SHM_ACK = nxt_msg_last(_NXT_PORT_MSG_SHM_ACK), diff --git a/src/nxt_port_socket.c b/src/nxt_port_socket.c index 3cf2e79a..ba1b7081 100644 --- a/src/nxt_port_socket.c +++ b/src/nxt_port_socket.c @@ -21,6 +21,7 @@ static nxt_int_t nxt_port_msg_chk_insert(nxt_task_t *task, nxt_port_t *port, static nxt_port_send_msg_t *nxt_port_msg_alloc(nxt_port_send_msg_t *m); static void nxt_port_write_handler(nxt_task_t *task, void *obj, void *data); static nxt_port_send_msg_t *nxt_port_msg_first(nxt_port_t *port); +nxt_inline void nxt_port_msg_close_fd(nxt_port_send_msg_t *msg); static nxt_buf_t *nxt_port_buf_completion(nxt_task_t *task, nxt_work_queue_t *wq, nxt_buf_t *b, size_t sent, nxt_bool_t mmap_mode); static nxt_port_send_msg_t *nxt_port_msg_insert_tail(nxt_port_t *port, @@ -449,19 +450,7 @@ next_fragment: goto fail; } - if (msg->close_fd) { - if (msg->fd[0] != -1) { - nxt_fd_close(msg->fd[0]); - - msg->fd[0] = -1; - } - - if (msg->fd[1] != -1) { - nxt_fd_close(msg->fd[1]); - - msg->fd[1] = -1; - } - } + nxt_port_msg_close_fd(msg); msg->buf = nxt_port_buf_completion(task, wq, msg->buf, plain_size, m == NXT_PORT_METHOD_MMAP); @@ -524,6 +513,12 @@ next_fragment: } else { if (nxt_slow_path(n == NXT_ERROR)) { + if (msg->link.next == NULL) { + nxt_port_msg_close_fd(msg); + + nxt_port_release_send_msg(msg); + } + goto fail; } @@ -591,6 +586,27 @@ nxt_port_msg_first(nxt_port_t *port) } +nxt_inline void +nxt_port_msg_close_fd(nxt_port_send_msg_t *msg) +{ + if (!msg->close_fd) { + return; + } + + if (msg->fd[0] != -1) { + nxt_fd_close(msg->fd[0]); + + msg->fd[0] = -1; + } + + if (msg->fd[1] != -1) { + nxt_fd_close(msg->fd[1]); + + msg->fd[1] = -1; + } +} + + static nxt_buf_t * nxt_port_buf_completion(nxt_task_t *task, nxt_work_queue_t *wq, nxt_buf_t *b, size_t sent, nxt_bool_t mmap_mode) @@ -1315,19 +1331,7 @@ nxt_port_error_handler(nxt_task_t *task, void *obj, void *data) nxt_queue_each(msg, &port->messages, nxt_port_send_msg_t, link) { - if (msg->close_fd) { - if (msg->fd[0] != -1) { - nxt_fd_close(msg->fd[0]); - - msg->fd[0] = -1; - } - - if (msg->fd[1] != -1) { - nxt_fd_close(msg->fd[1]); - - msg->fd[1] = -1; - } - } + nxt_port_msg_close_fd(msg); for (b = msg->buf; b != NULL; b = next) { next = b->next; diff --git a/src/nxt_router.c b/src/nxt_router.c index 015ae226..39d375f8 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -18,6 +18,8 @@ #include <nxt_app_queue.h> #include <nxt_port_queue.h> +#define NXT_SHARED_PORT_ID 0xFFFFu + typedef struct { nxt_str_t type; uint32_t processes; @@ -44,10 +46,10 @@ typedef struct { nxt_str_t name; nxt_socket_conf_t *socket_conf; nxt_router_temp_conf_t *temp_conf; - nxt_conf_value_t *conf_cmds; + nxt_tls_init_t *tls_init; nxt_bool_t last; - nxt_queue_link_t link; /* for nxt_socket_conf_t.tls */ + nxt_queue_link_t link; /* for nxt_socket_conf_t.tls */ } nxt_router_tlssock_t; #endif @@ -67,6 +69,12 @@ typedef struct { } nxt_app_rpc_t; +typedef struct { + nxt_app_joint_t *app_joint; + uint32_t generation; +} nxt_app_joint_rpc_t; + + static nxt_int_t nxt_router_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp); static nxt_int_t nxt_router_start(nxt_task_t *task, nxt_process_data_t *data); @@ -79,6 +87,8 @@ static void nxt_router_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); static void nxt_router_conf_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); +static void nxt_router_app_restart_handler(nxt_task_t *task, + nxt_port_recv_msg_t *msg); static void nxt_router_remove_pid_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); static void nxt_router_access_log_reopen_handler(nxt_task_t *task, @@ -97,6 +107,9 @@ static nxt_int_t nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, u_char *start, u_char *end); static nxt_int_t nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, nxt_conf_value_t *conf); +static nxt_int_t nxt_router_conf_process_client_ip(nxt_task_t *task, + nxt_router_temp_conf_t *tmcf, nxt_socket_conf_t *skcf, + nxt_conf_value_t *conf); static nxt_app_t *nxt_router_app_find(nxt_queue_t *queue, nxt_str_t *name); static nxt_int_t nxt_router_apps_hash_test(nxt_lvlhsh_query_t *lhq, void *data); @@ -123,8 +136,8 @@ static void nxt_router_listen_socket_error(nxt_task_t *task, 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 * conf_cmds); + nxt_conf_value_t *value, nxt_socket_conf_t *skcf, nxt_tls_init_t *tls_init, + nxt_bool_t last); #endif static void nxt_router_app_rpc_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_app_t *app); @@ -224,8 +237,6 @@ static void nxt_router_http_request_error(nxt_task_t *task, void *obj, static void nxt_router_http_request_done(nxt_task_t *task, void *obj, void *data); -static void nxt_router_dummy_buf_completion(nxt_task_t *task, void *obj, - void *data); static void nxt_router_app_prepare_request(nxt_task_t *task, nxt_request_rpc_data_t *req_rpc_data); static nxt_buf_t *nxt_router_prepare_msg(nxt_task_t *task, @@ -281,6 +292,7 @@ static const nxt_port_handlers_t nxt_router_process_port_handlers = { .mmap = nxt_port_mmap_handler, .get_mmap = nxt_router_get_mmap_handler, .data = nxt_router_conf_data_handler, + .app_restart = nxt_router_app_restart_handler, .remove_pid = nxt_router_remove_pid_handler, .access_log = nxt_router_access_log_reopen_handler, .rpc_ready = nxt_port_rpc_handler, @@ -379,14 +391,15 @@ static void nxt_router_start_app_process_handler(nxt_task_t *task, nxt_port_t *port, void *data) { - size_t size; - uint32_t stream; - nxt_mp_t *mp; - nxt_int_t ret; - nxt_app_t *app; - nxt_buf_t *b; - nxt_port_t *main_port; - nxt_runtime_t *rt; + size_t size; + uint32_t stream; + nxt_mp_t *mp; + nxt_int_t ret; + nxt_app_t *app; + nxt_buf_t *b; + nxt_port_t *main_port; + nxt_runtime_t *rt; + nxt_app_joint_rpc_t *app_joint_rpc; app = data; @@ -407,30 +420,29 @@ nxt_router_start_app_process_handler(nxt_task_t *task, nxt_port_t *port, *b->mem.free++ = '\0'; nxt_buf_cpystr(b, &app->conf); - nxt_router_app_joint_use(task, app->joint, 1); - - stream = nxt_port_rpc_register_handler(task, port, - nxt_router_app_port_ready, - nxt_router_app_port_error, - -1, app->joint); - - if (nxt_slow_path(stream == 0)) { - nxt_router_app_joint_use(task, app->joint, -1); - + app_joint_rpc = nxt_port_rpc_register_handler_ex(task, port, + nxt_router_app_port_ready, + nxt_router_app_port_error, + sizeof(nxt_app_joint_rpc_t)); + if (nxt_slow_path(app_joint_rpc == NULL)) { goto failed; } + stream = nxt_port_rpc_ex_stream(app_joint_rpc); + ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_PROCESS, -1, stream, port->id, b); - if (nxt_slow_path(ret != NXT_OK)) { nxt_port_rpc_cancel(task, port, stream); - nxt_router_app_joint_use(task, app->joint, -1); - goto failed; } + app_joint_rpc->app_joint = app->joint; + app_joint_rpc->generation = app->generation; + + nxt_router_app_joint_use(task, app->joint, 1); + nxt_router_app_use(task, app, -1); return; @@ -504,6 +516,7 @@ nxt_router_msg_cancel(nxt_task_t *task, nxt_request_rpc_data_t *req_rpc_data) { nxt_buf_t *b, *next; nxt_bool_t cancelled; + nxt_port_t *app_port; nxt_msg_info_t *msg_info; msg_info = &req_rpc_data->msg_info; @@ -512,13 +525,20 @@ nxt_router_msg_cancel(nxt_task_t *task, nxt_request_rpc_data_t *req_rpc_data) return 0; } - cancelled = nxt_app_queue_cancel(req_rpc_data->app->shared_port->queue, - msg_info->tracking_cookie, - req_rpc_data->stream); + app_port = req_rpc_data->app_port; + + if (app_port != NULL && app_port->id == NXT_SHARED_PORT_ID) { + cancelled = nxt_app_queue_cancel(app_port->queue, + msg_info->tracking_cookie, + req_rpc_data->stream); - if (cancelled) { - nxt_debug(task, "stream #%uD: cancelled by router", - req_rpc_data->stream); + if (cancelled) { + nxt_debug(task, "stream #%uD: cancelled by router", + req_rpc_data->stream); + } + + } else { + cancelled = 0; } for (b = msg_info->buf; b != NULL; b = next) { @@ -680,18 +700,20 @@ nxt_router_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_assert(main_app_port != NULL); app = main_app_port->app; - nxt_assert(app != NULL); - nxt_thread_mutex_lock(&app->mutex); + if (nxt_fast_path(app != NULL)) { + nxt_thread_mutex_lock(&app->mutex); - /* TODO here should be find-and-add code because there can be - port waiters in port_hash */ - nxt_port_hash_add(&app->port_hash, port); - app->port_hash_count++; + /* TODO here should be find-and-add code because there can be + port waiters in port_hash */ + nxt_port_hash_add(&app->port_hash, port); + app->port_hash_count++; - nxt_thread_mutex_unlock(&app->mutex); + nxt_thread_mutex_unlock(&app->mutex); + + port->app = app; + } - port->app = app; port->main_app_port = main_app_port; nxt_port_socket_write(task, port, NXT_PORT_MSG_PORT_ACK, -1, 0, 0, NULL); @@ -792,6 +814,90 @@ cleanup: static void +nxt_router_app_restart_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + nxt_app_t *app; + nxt_int_t ret; + nxt_str_t app_name; + nxt_port_t *port, *reply_port, *shared_port, *old_shared_port; + nxt_port_msg_type_t reply; + + reply_port = nxt_runtime_port_find(task->thread->runtime, + msg->port_msg.pid, + msg->port_msg.reply_port); + if (nxt_slow_path(reply_port == NULL)) { + nxt_alert(task, "app_restart_handler: reply port not found"); + return; + } + + app_name.length = nxt_buf_mem_used_size(&msg->buf->mem); + app_name.start = msg->buf->mem.pos; + + nxt_debug(task, "app_restart_handler: %V", &app_name); + + app = nxt_router_app_find(&nxt_router->apps, &app_name); + + if (nxt_fast_path(app != NULL)) { + shared_port = nxt_port_new(task, NXT_SHARED_PORT_ID, nxt_pid, + NXT_PROCESS_APP); + if (nxt_slow_path(shared_port == NULL)) { + goto fail; + } + + ret = nxt_port_socket_init(task, shared_port, 0); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_port_use(task, shared_port, -1); + goto fail; + } + + ret = nxt_router_app_queue_init(task, shared_port); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_port_write_close(shared_port); + nxt_port_read_close(shared_port); + nxt_port_use(task, shared_port, -1); + goto fail; + } + + nxt_port_write_enable(task, shared_port); + + nxt_thread_mutex_lock(&app->mutex); + + nxt_queue_each(port, &app->ports, nxt_port_t, app_link) { + + (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1, + 0, 0, NULL); + + } nxt_queue_loop; + + app->generation++; + + shared_port->app = app; + + old_shared_port = app->shared_port; + old_shared_port->app = NULL; + + app->shared_port = shared_port; + + nxt_thread_mutex_unlock(&app->mutex); + + nxt_port_close(task, old_shared_port); + nxt_port_use(task, old_shared_port, -1); + + reply = NXT_PORT_MSG_RPC_READY_LAST; + + } else { + +fail: + + reply = NXT_PORT_MSG_RPC_ERROR; + } + + nxt_port_socket_write(task, reply_port, reply, -1, msg->port_msg.stream, + 0, NULL); +} + + +static void nxt_router_app_process_remove_pid(nxt_task_t *task, nxt_port_t *port, void *data) { @@ -956,8 +1062,6 @@ nxt_router_conf_apply(nxt_task_t *task, void *obj, void *data) tls = nxt_queue_link_data(qlk, nxt_router_tlssock_t, link); - 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; @@ -1341,12 +1445,13 @@ 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, *conf_cmds; + nxt_tls_init_t *tls_init; + nxt_conf_value_t *certificate; #endif nxt_conf_value_t *conf, *http, *value, *websocket; nxt_conf_value_t *applications, *application; nxt_conf_value_t *listeners, *listener; - nxt_conf_value_t *routes_conf, *static_conf; + nxt_conf_value_t *routes_conf, *static_conf, *client_ip_conf; nxt_socket_conf_t *skcf; nxt_http_routes_t *routes; nxt_event_engine_t *engine; @@ -1363,9 +1468,13 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, #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"); + static nxt_str_t conf_cache_path = nxt_string("/tls/session/cache_size"); + static nxt_str_t conf_timeout_path = nxt_string("/tls/session/timeout"); + static nxt_str_t conf_tickets = nxt_string("/tls/session/tickets"); #endif static nxt_str_t static_path = nxt_string("/settings/http/static"); static nxt_str_t websocket_path = nxt_string("/settings/http/websocket"); + static nxt_str_t client_ip_path = nxt_string("/client_ip"); conf = nxt_conf_json_parse(tmcf->mem_pool, start, end, NULL); if (conf == NULL) { @@ -1604,7 +1713,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, app_joint->free_app_work.task = &engine->task; app_joint->free_app_work.obj = app_joint; - port = nxt_port_new(task, (nxt_port_id_t) -1, nxt_pid, + port = nxt_port_new(task, NXT_SHARED_PORT_ID, nxt_pid, NXT_PROCESS_APP); if (nxt_slow_path(port == NULL)) { return NXT_ERROR; @@ -1737,11 +1846,40 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, t->length = nxt_strlen(t->start); } + client_ip_conf = nxt_conf_get_path(listener, &client_ip_path); + ret = nxt_router_conf_process_client_ip(task, tmcf, skcf, + client_ip_conf); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + #if (NXT_TLS) certificate = nxt_conf_get_path(listener, &certificate_path); if (certificate != NULL) { - conf_cmds = nxt_conf_get_path(listener, &conf_commands_path); + tls_init = nxt_mp_get(tmcf->mem_pool, sizeof(nxt_tls_init_t)); + if (nxt_slow_path(tls_init == NULL)) { + return NXT_ERROR; + } + + tls_init->cache_size = 0; + tls_init->timeout = 300; + + value = nxt_conf_get_path(listener, &conf_cache_path); + if (value != NULL) { + tls_init->cache_size = nxt_conf_get_number(value); + } + + value = nxt_conf_get_path(listener, &conf_timeout_path); + if (value != NULL) { + tls_init->timeout = nxt_conf_get_number(value); + } + + tls_init->conf_cmds = nxt_conf_get_path(listener, + &conf_commands_path); + + tls_init->tickets_conf = nxt_conf_get_path(listener, + &conf_tickets); if (nxt_conf_type(certificate) == NXT_CONF_ARRAY) { n = nxt_conf_array_elements_count(certificate); @@ -1752,7 +1890,7 @@ 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, - conf_cmds); + tls_init, i == 0); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } @@ -1761,7 +1899,7 @@ 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, - conf_cmds); + tls_init, 1); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } @@ -1856,7 +1994,7 @@ 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 *conf_cmds) + nxt_tls_init_t *tls_init, nxt_bool_t last) { nxt_router_tlssock_t *tls; @@ -1865,9 +2003,10 @@ nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf, return NXT_ERROR; } + tls->tls_init = tls_init; tls->socket_conf = skcf; - tls->conf_cmds = conf_cmds; tls->temp_conf = tmcf; + tls->last = last; nxt_conf_get_string(value, &tls->name); nxt_queue_insert_tail(&tmcf->tls, &tls->link); @@ -1884,7 +2023,7 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, { uint32_t next, i; nxt_mp_t *mp; - nxt_str_t *type, extension, str; + nxt_str_t *type, exten, str; nxt_int_t ret; nxt_uint_t exts; nxt_conf_value_t *mtypes_conf, *ext_conf, *value; @@ -1922,12 +2061,12 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, if (nxt_conf_type(ext_conf) == NXT_CONF_STRING) { nxt_conf_get_string(ext_conf, &str); - if (nxt_slow_path(nxt_str_dup(mp, &extension, &str) == NULL)) { + if (nxt_slow_path(nxt_str_dup(mp, &exten, &str) == NULL)) { return NXT_ERROR; } ret = nxt_http_static_mtypes_hash_add(mp, &rtcf->mtypes_hash, - &extension, type); + &exten, type); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } @@ -1942,12 +2081,12 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, nxt_conf_get_string(value, &str); - if (nxt_slow_path(nxt_str_dup(mp, &extension, &str) == NULL)) { + if (nxt_slow_path(nxt_str_dup(mp, &exten, &str) == NULL)) { return NXT_ERROR; } ret = nxt_http_static_mtypes_hash_add(mp, &rtcf->mtypes_hash, - &extension, type); + &exten, type); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } @@ -1959,6 +2098,79 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, } +static nxt_int_t +nxt_router_conf_process_client_ip(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_socket_conf_t *skcf, nxt_conf_value_t *conf) +{ + char c; + size_t i; + nxt_mp_t *mp; + uint32_t hash; + nxt_str_t header; + nxt_conf_value_t *source_conf, *header_conf, *recursive_conf; + nxt_http_client_ip_t *client_ip; + nxt_http_route_addr_rule_t *source; + + static nxt_str_t header_path = nxt_string("/header"); + static nxt_str_t source_path = nxt_string("/source"); + static nxt_str_t recursive_path = nxt_string("/recursive"); + + if (conf == NULL) { + skcf->client_ip = NULL; + + return NXT_OK; + } + + mp = tmcf->router_conf->mem_pool; + + source_conf = nxt_conf_get_path(conf, &source_path); + header_conf = nxt_conf_get_path(conf, &header_path); + recursive_conf = nxt_conf_get_path(conf, &recursive_path); + + if (source_conf == NULL || header_conf == NULL) { + return NXT_ERROR; + } + + client_ip = nxt_mp_zget(mp, sizeof(nxt_http_client_ip_t)); + if (nxt_slow_path(client_ip == NULL)) { + return NXT_ERROR; + } + + source = nxt_http_route_addr_rule_create(task, mp, source_conf); + if (nxt_slow_path(source == NULL)) { + return NXT_ERROR; + } + + client_ip->source = source; + + nxt_conf_get_string(header_conf, &header); + + if (recursive_conf != NULL) { + client_ip->recursive = nxt_conf_get_boolean(recursive_conf); + } + + client_ip->header = nxt_str_dup(mp, NULL, &header); + if (nxt_slow_path(client_ip->header == NULL)) { + return NXT_ERROR; + } + + hash = NXT_HTTP_FIELD_HASH_INIT; + + for (i = 0; i < client_ip->header->length; i++) { + c = client_ip->header->start[i]; + hash = nxt_http_field_hash_char(hash, nxt_lowcase(c)); + } + + hash = nxt_http_field_hash_end(hash) & 0xFFFF; + + client_ip->header_hash = hash; + + skcf->client_ip = client_ip; + + return NXT_OK; +} + + static nxt_app_t * nxt_router_app_find(nxt_queue_t *queue, nxt_str_t *name) { @@ -2135,21 +2347,46 @@ nxt_router_apps_hash_use(nxt_task_t *task, nxt_router_conf_t *rtcf, int i) } +typedef struct { + nxt_app_t *app; + nxt_int_t target; +} nxt_http_app_conf_t; + nxt_int_t -nxt_router_listener_application(nxt_router_conf_t *rtcf, nxt_str_t *name, - nxt_http_action_t *action) +nxt_router_application_init(nxt_router_conf_t *rtcf, nxt_str_t *name, + nxt_str_t *target, nxt_http_action_t *action) { - nxt_app_t *app; + nxt_app_t *app; + nxt_str_t *targets; + nxt_uint_t i; + nxt_http_app_conf_t *conf; app = nxt_router_apps_hash_get(rtcf, name); - if (app == NULL) { return NXT_DECLINED; } - action->u.app.application = app; + conf = nxt_mp_get(rtcf->mem_pool, sizeof(nxt_http_app_conf_t)); + if (nxt_slow_path(conf == NULL)) { + return NXT_ERROR; + } + action->handler = nxt_http_application_handler; + action->u.conf = conf; + + conf->app = app; + + if (target != NULL && target->length != 0) { + targets = app->targets; + + for (i = 0; !nxt_strstr_eq(target, &targets[i]); i++); + + conf->target = i; + + } else { + conf->target = 0; + } return NXT_OK; } @@ -2297,7 +2534,7 @@ nxt_router_listen_socket_rpc_create(nxt_task_t *task, goto fail; } - b->completion_handler = nxt_router_dummy_buf_completion; + b->completion_handler = nxt_buf_dummy_completion; b->mem.free = nxt_cpymem(b->mem.free, skcf->listen->sockaddr, size); @@ -2466,6 +2703,8 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, tlscf = tls->socket_conf->tls; } + tls->tls_init->conf = tlscf; + bundle = nxt_mp_get(mp, sizeof(nxt_tls_bundle_conf_t)); if (nxt_slow_path(bundle == NULL)) { goto fail; @@ -2479,8 +2718,8 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, bundle->next = tlscf->bundle; tlscf->bundle = bundle; - ret = task->thread->runtime->tls->server_init(task, tlscf, mp, - tls->conf_cmds, tls->last); + ret = task->thread->runtime->tls->server_init(task, mp, tls->tls_init, + tls->last); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } @@ -2526,7 +2765,7 @@ nxt_router_app_rpc_create(nxt_task_t *task, goto fail; } - b->completion_handler = nxt_router_dummy_buf_completion; + b->completion_handler = nxt_buf_dummy_completion; nxt_buf_cpystr(b, &app->name); *b->mem.free++ = '\0'; @@ -3555,7 +3794,7 @@ nxt_router_access_log_open(nxt_task_t *task, nxt_router_temp_conf_t *tmcf) goto fail; } - b->completion_handler = nxt_router_dummy_buf_completion; + b->completion_handler = nxt_buf_dummy_completion; nxt_buf_cpystr(b, &access_log->path); *b->mem.free++ = '\0'; @@ -4183,11 +4422,16 @@ static void nxt_router_app_port_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) { - nxt_app_t *app; - nxt_port_t *port; - nxt_app_joint_t *app_joint; + nxt_app_t *app; + nxt_bool_t start_process; + nxt_port_t *port; + nxt_app_joint_t *app_joint; + nxt_app_joint_rpc_t *app_joint_rpc; + + nxt_assert(data != NULL); - app_joint = data; + app_joint_rpc = data; + app_joint = app_joint_rpc->app_joint; port = msg->u.new_port; nxt_assert(app_joint != NULL); @@ -4207,14 +4451,37 @@ nxt_router_app_port_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg, return; } - port->app = app; - port->main_app_port = port; - nxt_thread_mutex_lock(&app->mutex); nxt_assert(app->pending_processes != 0); app->pending_processes--; + + if (nxt_slow_path(app->generation != app_joint_rpc->generation)) { + nxt_debug(task, "new port ready for restarted app, send QUIT"); + + start_process = !task->thread->engine->shutdown + && nxt_router_app_can_start(app) + && nxt_router_app_need_start(app); + + if (start_process) { + app->pending_processes++; + } + + nxt_thread_mutex_unlock(&app->mutex); + + nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1, 0, 0, NULL); + + if (start_process) { + nxt_router_start_app_process(task, app); + } + + return; + } + + port->app = app; + port->main_app_port = port; + app->processes++; nxt_port_hash_add(&app->port_hash, port); app->port_hash_count++; @@ -4268,12 +4535,16 @@ static void nxt_router_app_port_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) { - nxt_app_t *app; - nxt_app_joint_t *app_joint; - nxt_queue_link_t *link; - nxt_http_request_t *r; + nxt_app_t *app; + nxt_app_joint_t *app_joint; + nxt_queue_link_t *link; + nxt_http_request_t *r; + nxt_app_joint_rpc_t *app_joint_rpc; + + nxt_assert(data != NULL); - app_joint = data; + app_joint_rpc = data; + app_joint = app_joint_rpc->app_joint; nxt_assert(app_joint != NULL); @@ -4440,7 +4711,7 @@ nxt_router_app_port_release(nxt_task_t *task, nxt_port_t *port, port->pid, port->id, (int) inc_use, (int) got_response); - if (port == app->shared_port) { + if (port->id == NXT_SHARED_PORT_ID) { nxt_thread_mutex_lock(&app->mutex); app->active_requests -= got_response + dec_requests; @@ -4810,6 +5081,8 @@ nxt_router_free_app(nxt_task_t *task, void *obj, void *data) app->shared_port->app = NULL; nxt_port_close(task, app->shared_port); nxt_port_use(task, app->shared_port, -1); + + app->shared_port = NULL; } nxt_thread_mutex_destroy(&app->mutex); @@ -4876,13 +5149,17 @@ nxt_router_app_port_get(nxt_task_t *task, nxt_app_t *app, void nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r, - nxt_app_t *app) + nxt_http_action_t *action) { nxt_event_engine_t *engine; + nxt_http_app_conf_t *conf; nxt_request_rpc_data_t *req_rpc_data; + conf = action->u.conf; engine = task->thread->engine; + r->app_target = conf->target; + req_rpc_data = nxt_port_rpc_register_handler_ex(task, engine->port, nxt_router_response_ready_handler, nxt_router_response_error_handler, @@ -4913,11 +5190,11 @@ nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r, r->err_work.obj = r; req_rpc_data->stream = nxt_port_rpc_ex_stream(req_rpc_data); - req_rpc_data->app = app; + req_rpc_data->app = conf->app; req_rpc_data->msg_info.body_fd = -1; req_rpc_data->rpc_cancel = 1; - nxt_router_app_use(task, app, 1); + nxt_router_app_use(task, conf->app, 1); req_rpc_data->request = r; r->req_rpc_data = req_rpc_data; @@ -4926,7 +5203,7 @@ nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r, r->last->completion_handler = nxt_router_http_request_done; } - nxt_router_app_port_get(task, app, req_rpc_data); + nxt_router_app_port_get(task, conf->app, req_rpc_data); nxt_router_app_prepare_request(task, req_rpc_data); } @@ -4968,12 +5245,6 @@ nxt_router_http_request_done(nxt_task_t *task, void *obj, void *data) static void -nxt_router_dummy_buf_completion(nxt_task_t *task, void *obj, void *data) -{ -} - - -static void nxt_router_app_prepare_request(nxt_task_t *task, nxt_request_rpc_data_t *req_rpc_data) { diff --git a/src/nxt_router.h b/src/nxt_router.h index b1ccdf51..fc068b53 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -18,6 +18,7 @@ typedef struct nxt_http_request_s nxt_http_request_t; typedef struct nxt_http_action_s nxt_http_action_t; typedef struct nxt_http_routes_s nxt_http_routes_t; +typedef struct nxt_http_client_ip_s nxt_http_client_ip_t; typedef struct nxt_upstream_s nxt_upstream_t; typedef struct nxt_upstreams_s nxt_upstreams_t; typedef struct nxt_router_access_log_s nxt_router_access_log_t; @@ -125,6 +126,8 @@ struct nxt_app_s { uint32_t max_pending_processes; uint32_t max_requests; + uint32_t generation; + nxt_msec_t timeout; nxt_msec_t idle_timeout; @@ -193,6 +196,8 @@ typedef struct { uint8_t discard_unsafe_fields; /* 1 bit */ + nxt_http_client_ip_t *client_ip; + #if (NXT_TLS) nxt_tls_conf_t *tls; #endif @@ -223,10 +228,10 @@ struct nxt_router_access_log_s { void nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r, - nxt_app_t *app); + nxt_http_action_t *action); void nxt_router_app_port_close(nxt_task_t *task, nxt_port_t *port); -nxt_int_t nxt_router_listener_application(nxt_router_conf_t *rtcf, - nxt_str_t *name, nxt_http_action_t *action); +nxt_int_t nxt_router_application_init(nxt_router_conf_t *rtcf, nxt_str_t *name, + nxt_str_t *target, nxt_http_action_t *action); void nxt_router_listen_event_release(nxt_task_t *task, nxt_listen_event_t *lev, nxt_socket_conf_joint_t *joint); void nxt_router_conf_release(nxt_task_t *task, nxt_socket_conf_joint_t *joint); diff --git a/src/nxt_sockaddr.c b/src/nxt_sockaddr.c index af696a6b..730428e4 100644 --- a/src/nxt_sockaddr.c +++ b/src/nxt_sockaddr.c @@ -525,9 +525,9 @@ nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end) return buf; } - zero_start = 8; + zero_start = 16; zero_groups = 0; - last_zero_start = 8; + last_zero_start = 16; last_zero_groups = 0; for (i = 0; i < 16; i += 2) { @@ -605,10 +605,35 @@ nxt_sockaddr_parse(nxt_mp_t *mp, nxt_str_t *addr) { nxt_sockaddr_t *sa; + sa = nxt_sockaddr_parse_optport(mp, addr); + + if (sa != NULL + && sa->u.sockaddr.sa_family != AF_UNIX + && nxt_sockaddr_port_number(sa) == 0) + { + nxt_thread_log_error(NXT_LOG_ERR, + "The address \"%V\" must specify a port.", addr); + return NULL; + } + + return sa; +} + + +nxt_sockaddr_t * +nxt_sockaddr_parse_optport(nxt_mp_t *mp, nxt_str_t *addr) +{ + nxt_sockaddr_t *sa; + + if (addr->length == 0) { + nxt_thread_log_error(NXT_LOG_ERR, "socket address cannot be empty"); + return NULL; + } + if (addr->length > 6 && nxt_memcmp(addr->start, "unix:", 5) == 0) { sa = nxt_sockaddr_unix_parse(mp, addr); - } else if (addr->length != 0 && addr->start[0] == '[') { + } else if (addr->start[0] == '[' || nxt_inet6_probe(addr)) { sa = nxt_sockaddr_inet6_parse(mp, addr); } else { @@ -703,44 +728,60 @@ nxt_sockaddr_inet6_parse(nxt_mp_t *mp, nxt_str_t *addr) nxt_int_t ret, port; nxt_sockaddr_t *sa; - length = addr->length - 1; - start = addr->start + 1; + if (addr->start[0] == '[') { + length = addr->length - 1; + start = addr->start + 1; - end = nxt_memchr(start, ']', length); - - if (end != NULL) { - sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6), - NXT_INET6_ADDR_STR_LEN); - if (nxt_slow_path(sa == NULL)) { + end = nxt_memchr(start, ']', length); + if (nxt_slow_path(end == NULL)) { return NULL; } - ret = nxt_inet6_addr(&sa->u.sockaddr_in6.sin6_addr, start, end - start); + p = end + 1; + + } else { + length = addr->length; + start = addr->start; + end = addr->start + addr->length; + p = NULL; + } - if (nxt_fast_path(ret == NXT_OK)) { - p = end + 1; - length = (start + length) - p; + port = 0; - if (length > 2 && *p == ':') { - port = nxt_int_parse(p + 1, length - 1); + if (p != NULL) { + length = (start + length) - p; - if (port > 0 && port < 65536) { - sa->u.sockaddr_in6.sin6_port = htons((in_port_t) port); - sa->u.sockaddr_in6.sin6_family = AF_INET6; + if (length < 2 || *p != ':') { + nxt_thread_log_error(NXT_LOG_ERR, "invalid IPv6 address in \"%V\"", + addr); + return NULL; + } - return sa; - } - } + port = nxt_int_parse(p + 1, length - 1); + if (port < 1 || port > 65535) { nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr); - return NULL; } } - nxt_thread_log_error(NXT_LOG_ERR, "invalid IPv6 address in \"%V\"", addr); + sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6), + NXT_INET6_ADDR_STR_LEN); + if (nxt_slow_path(sa == NULL)) { + return NULL; + } - return NULL; + ret = nxt_inet6_addr(&sa->u.sockaddr_in6.sin6_addr, start, end - start); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_thread_log_error(NXT_LOG_ERR, "invalid IPv6 address in \"%V\"", + addr); + return NULL; + } + + sa->u.sockaddr_in6.sin6_family = AF_INET6; + sa->u.sockaddr_in6.sin6_port = htons((in_port_t) port); + + return sa; #else /* !(NXT_INET6) */ @@ -763,41 +804,48 @@ nxt_sockaddr_inet_parse(nxt_mp_t *mp, nxt_str_t *addr) p = nxt_memchr(addr->start, ':', addr->length); - if (nxt_fast_path(p != NULL)) { - inaddr = INADDR_ANY; + if (p == NULL) { + length = addr->length; + + } else { length = p - addr->start; + } - if (length != 1 || addr->start[0] != '*') { - inaddr = nxt_inet_addr(addr->start, length); + inaddr = INADDR_ANY; - if (nxt_slow_path(inaddr == INADDR_NONE)) { - nxt_thread_log_error(NXT_LOG_ERR, "invalid address \"%V\"", - addr); - return NULL; - } + if (length != 1 || addr->start[0] != '*') { + inaddr = nxt_inet_addr(addr->start, length); + if (nxt_slow_path(inaddr == INADDR_NONE)) { + nxt_thread_log_error(NXT_LOG_ERR, "invalid address \"%V\"", addr); + return NULL; } + } + + port = 0; + if (p != NULL) { p++; length = (addr->start + addr->length) - p; - port = nxt_int_parse(p, length); - - if (port > 0 && port < 65536) { - sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in), - NXT_INET_ADDR_STR_LEN); - if (nxt_fast_path(sa != NULL)) { - sa->u.sockaddr_in.sin_family = AF_INET; - sa->u.sockaddr_in.sin_port = htons((in_port_t) port); - sa->u.sockaddr_in.sin_addr.s_addr = inaddr; - } + port = nxt_int_parse(p, length); - return sa; + if (port < 1 || port > 65535) { + nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr); + return NULL; } } - nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr); + sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in), + NXT_INET_ADDR_STR_LEN); + if (nxt_slow_path(sa == NULL)) { + return NULL; + } - return NULL; + sa->u.sockaddr_in.sin_family = AF_INET; + sa->u.sockaddr_in.sin_addr.s_addr = inaddr; + sa->u.sockaddr_in.sin_port = htons((in_port_t) port); + + return sa; } @@ -1320,3 +1368,19 @@ nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf, size_t length) } #endif + + +nxt_bool_t +nxt_inet6_probe(nxt_str_t *str) +{ + u_char *colon, *end; + + colon = nxt_memchr(str->start, ':', str->length); + + if (colon != NULL) { + end = str->start + str->length; + colon = nxt_memchr(colon + 1, ':', end - (colon + 1)); + } + + return (colon != NULL); +} diff --git a/src/nxt_sockaddr.h b/src/nxt_sockaddr.h index aa4da5d2..a8f1b393 100644 --- a/src/nxt_sockaddr.h +++ b/src/nxt_sockaddr.h @@ -91,12 +91,15 @@ NXT_EXPORT nxt_bool_t nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, NXT_EXPORT size_t nxt_sockaddr_ntop(nxt_sockaddr_t *sa, u_char *buf, u_char *end, nxt_bool_t port); NXT_EXPORT nxt_sockaddr_t *nxt_sockaddr_parse(nxt_mp_t *mp, nxt_str_t *addr); +NXT_EXPORT nxt_sockaddr_t *nxt_sockaddr_parse_optport(nxt_mp_t *mp, + nxt_str_t *addr); NXT_EXPORT void nxt_job_sockaddr_parse(nxt_job_sockaddr_parse_t *jbs); NXT_EXPORT in_addr_t nxt_inet_addr(u_char *buf, size_t len); #if (NXT_INET6) NXT_EXPORT nxt_int_t nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf, size_t len); #endif +NXT_EXPORT nxt_bool_t nxt_inet6_probe(nxt_str_t *addr); #define NXT_INET_ADDR_STR_LEN nxt_length("255.255.255.255:65535") diff --git a/src/nxt_tls.h b/src/nxt_tls.h index 63c49ee4..eeb4e7ba 100644 --- a/src/nxt_tls.h +++ b/src/nxt_tls.h @@ -28,14 +28,16 @@ typedef struct nxt_tls_conf_s nxt_tls_conf_t; typedef struct nxt_tls_bundle_conf_s nxt_tls_bundle_conf_t; +typedef struct nxt_tls_init_s nxt_tls_init_t; +typedef struct nxt_tls_ticket_s nxt_tls_ticket_t; +typedef struct nxt_tls_tickets_s nxt_tls_tickets_t; typedef struct { nxt_int_t (*library_init)(nxt_task_t *task); void (*library_free)(nxt_task_t *task); - 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_int_t (*server_init)(nxt_task_t *task, nxt_mp_t *mp, + nxt_tls_init_t *tls_init, nxt_bool_t last); void (*server_free)(nxt_task_t *task, nxt_tls_conf_t *conf); @@ -63,6 +65,8 @@ struct nxt_tls_conf_s { nxt_tls_bundle_conf_t *bundle; nxt_lvlhsh_t bundle_hash; + nxt_tls_tickets_t *tickets; + void (*conn_init)(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c); @@ -78,12 +82,38 @@ struct nxt_tls_conf_s { }; +struct nxt_tls_init_s { + size_t cache_size; + nxt_time_t timeout; + nxt_conf_value_t *conf_cmds; + nxt_conf_value_t *tickets_conf; + + nxt_tls_conf_t *conf; +}; + + +struct nxt_tls_ticket_s { + uint8_t aes128; + u_char name[16]; + u_char hmac_key[32]; + u_char aes_key[32]; +}; + + +struct nxt_tls_tickets_s { + nxt_uint_t count; + nxt_tls_ticket_t tickets[]; +}; + + #if (NXT_HAVE_OPENSSL) extern const nxt_tls_lib_t nxt_openssl_lib; void nxt_cdecl nxt_openssl_log_error(nxt_task_t *task, nxt_uint_t level, const char *fmt, ...); u_char *nxt_openssl_copy_error(u_char *p, u_char *end); +nxt_int_t nxt_openssl_base64_decode(u_char *d, size_t dlen, const u_char *s, + size_t slen); #endif #if (NXT_HAVE_GNUTLS) diff --git a/src/nxt_upstream.c b/src/nxt_upstream.c index 9f81b286..de9b1d49 100644 --- a/src/nxt_upstream.c +++ b/src/nxt_upstream.c @@ -78,6 +78,10 @@ nxt_upstream_find(nxt_upstreams_t *upstreams, nxt_str_t *name, uint32_t i, n; nxt_upstream_t *upstream; + if (upstreams == NULL) { + return NXT_DECLINED; + } + upstream = &upstreams->upstream[0]; n = upstreams->items; diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c index 588a147a..abb04194 100644 --- a/src/python/nxt_python.c +++ b/src/python/nxt_python.c @@ -264,7 +264,7 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) goto fail; } - rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data); + rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data, 1); if (nxt_slow_path(rc != NXT_UNIT_OK)) { goto fail; } @@ -364,13 +364,13 @@ nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target, 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); + callable, module_name); goto fail; } if (nxt_slow_path(PyCallable_Check(obj) == 0)) { nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object", - callable, module); + callable, module_name); goto fail; } @@ -504,7 +504,7 @@ nxt_python_init_threads(nxt_python_app_conf_t *c) for (i = 0; i < c->threads - 1; i++) { ti = &nxt_py_threads[i]; - res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data); + res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data, 0); if (nxt_slow_path(res != NXT_UNIT_OK)) { return NXT_UNIT_ERROR; } diff --git a/src/python/nxt_python.h b/src/python/nxt_python.h index a5c1d9a6..e4eac9dc 100644 --- a/src/python/nxt_python.h +++ b/src/python/nxt_python.h @@ -60,7 +60,7 @@ typedef struct { typedef struct { - int (*ctx_data_alloc)(void **pdata); + int (*ctx_data_alloc)(void **pdata, int main); void (*ctx_data_free)(void *data); int (*startup)(void *data); int (*run)(nxt_unit_ctx_t *ctx); diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c index 1d220678..26003805 100644 --- a/src/python/nxt_python_asgi.c +++ b/src/python/nxt_python_asgi.c @@ -17,7 +17,7 @@ static PyObject *nxt_python_asgi_get_func(PyObject *obj); -static int nxt_python_asgi_ctx_data_alloc(void **pdata); +static int nxt_python_asgi_ctx_data_alloc(void **pdata, int main); static void nxt_python_asgi_ctx_data_free(void *data); static int nxt_python_asgi_startup(void *data); static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx); @@ -194,10 +194,11 @@ nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) static int -nxt_python_asgi_ctx_data_alloc(void **pdata) +nxt_python_asgi_ctx_data_alloc(void **pdata, int main) { uint32_t i; - PyObject *asyncio, *loop, *new_event_loop, *obj; + PyObject *asyncio, *loop, *event_loop, *obj; + const char *event_loop_func; nxt_py_asgi_ctx_data_t *ctx_data; ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t)); @@ -232,23 +233,28 @@ nxt_python_asgi_ctx_data_alloc(void **pdata) goto fail; } - new_event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio), - "new_event_loop"); - if (nxt_slow_path(new_event_loop == NULL)) { + event_loop_func = main ? "get_event_loop" : "new_event_loop"; + + event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio), + event_loop_func); + if (nxt_slow_path(event_loop == NULL)) { nxt_unit_alert(NULL, - "Python failed to get 'new_event_loop' from module 'asyncio'"); + "Python failed to get '%s' from module 'asyncio'", + event_loop_func); goto fail; } - if (nxt_slow_path(PyCallable_Check(new_event_loop) == 0)) { + if (nxt_slow_path(PyCallable_Check(event_loop) == 0)) { nxt_unit_alert(NULL, - "'asyncio.new_event_loop' is not a callable object"); + "'asyncio.%s' is not a callable object", + event_loop_func); goto fail; } - loop = PyObject_CallObject(new_event_loop, NULL); + loop = PyObject_CallObject(event_loop, NULL); if (nxt_slow_path(loop == NULL)) { - nxt_unit_alert(NULL, "Python failed to call 'asyncio.new_event_loop'"); + nxt_unit_alert(NULL, "Python failed to call 'asyncio.%s'", + event_loop_func); goto fail; } diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c index d88c4b00..c4a77d53 100644 --- a/src/python/nxt_python_asgi_http.c +++ b/src/python/nxt_python_asgi_http.c @@ -23,10 +23,11 @@ typedef struct { PyObject *send_future; uint64_t content_length; uint64_t bytes_sent; - int complete; - int closed; PyObject *send_body; Py_ssize_t send_body_off; + uint8_t complete; + uint8_t closed; + uint8_t empty_body_received; } nxt_py_asgi_http_t; @@ -37,6 +38,9 @@ static PyObject *nxt_py_asgi_http_response_start(nxt_py_asgi_http_t *http, PyObject *dict); static PyObject *nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict); +static void nxt_py_asgi_http_emit_disconnect(nxt_py_asgi_http_t *http); +static void nxt_py_asgi_http_set_result(nxt_py_asgi_http_t *http, + PyObject *future, PyObject *msg); static PyObject *nxt_py_asgi_http_done(PyObject *self, PyObject *future); @@ -94,10 +98,11 @@ nxt_py_asgi_http_create(nxt_unit_request_info_t *req) http->send_future = NULL; http->content_length = -1; http->bytes_sent = 0; - http->complete = 0; - http->closed = 0; http->send_body = NULL; http->send_body_off = 0; + http->complete = 0; + http->closed = 0; + http->empty_body_received = 0; } return (PyObject *) http; @@ -117,7 +122,7 @@ nxt_py_asgi_http_receive(PyObject *self, PyObject *none) nxt_unit_req_debug(req, "asgi_http_receive"); - if (nxt_slow_path(http->closed || nxt_unit_response_is_sent(req))) { + if (nxt_slow_path(http->closed || http->complete )) { msg = nxt_py_asgi_new_msg(req, nxt_py_http_disconnect_str); } else { @@ -171,6 +176,14 @@ nxt_py_asgi_http_read_msg(nxt_py_asgi_http_t *http) size = nxt_py_asgi_http_body_buf_size; } + if (size == 0) { + if (http->empty_body_received) { + Py_RETURN_NONE; + } + + http->empty_body_received = 1; + } + if (size > 0) { body = PyBytes_FromStringAndSize(NULL, size); if (nxt_slow_path(body == NULL)) { @@ -442,6 +455,8 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) if (more_body == NULL || more_body == Py_False) { http->complete = 1; + + nxt_py_asgi_http_emit_disconnect(http); } Py_INCREF(http); @@ -449,10 +464,67 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) } +static void +nxt_py_asgi_http_emit_disconnect(nxt_py_asgi_http_t *http) +{ + PyObject *msg, *future; + + if (http->receive_future == NULL) { + return; + } + + msg = nxt_py_asgi_new_msg(http->req, nxt_py_http_disconnect_str); + if (nxt_slow_path(msg == NULL)) { + return; + } + + if (msg == Py_None) { + Py_DECREF(msg); + return; + } + + future = http->receive_future; + http->receive_future = NULL; + + nxt_py_asgi_http_set_result(http, future, msg); + + Py_DECREF(msg); +} + + +static void +nxt_py_asgi_http_set_result(nxt_py_asgi_http_t *http, PyObject *future, + PyObject *msg) +{ + PyObject *res; + + res = PyObject_CallMethodObjArgs(future, nxt_py_done_str, NULL); + if (nxt_slow_path(res == NULL)) { + nxt_unit_req_alert(http->req, "'done' call failed"); + nxt_python_print_exception(); + } + + if (nxt_fast_path(res == Py_False)) { + res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, + NULL); + if (nxt_slow_path(res == NULL)) { + nxt_unit_req_alert(http->req, "'set_result' call failed"); + nxt_python_print_exception(); + } + + } else { + res = NULL; + } + + Py_XDECREF(res); + Py_DECREF(future); +} + + void nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req) { - PyObject *msg, *future, *res; + PyObject *msg, *future; nxt_py_asgi_http_t *http; http = req->data; @@ -476,14 +548,7 @@ nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req) future = http->receive_future; http->receive_future = NULL; - res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_req_alert(req, "'set_result' call failed"); - nxt_python_print_exception(); - } - - Py_XDECREF(res); - Py_DECREF(future); + nxt_py_asgi_http_set_result(http, future, msg); Py_DECREF(msg); } @@ -527,15 +592,7 @@ nxt_py_asgi_http_drain(nxt_queue_link_t *lnk) future = http->send_future; http->send_future = NULL; - res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, Py_None, - NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_req_alert(http->req, "'set_result' call failed"); - nxt_python_print_exception(); - } - - Py_XDECREF(res); - Py_DECREF(future); + nxt_py_asgi_http_set_result(http, future, Py_None); return NXT_UNIT_OK; @@ -573,7 +630,6 @@ fail: void nxt_py_asgi_http_close_handler(nxt_unit_request_info_t *req) { - PyObject *msg, *future, *res; nxt_py_asgi_http_t *http; http = req->data; @@ -582,33 +638,7 @@ nxt_py_asgi_http_close_handler(nxt_unit_request_info_t *req) http->closed = 1; - if (http->receive_future == NULL) { - return; - } - - msg = nxt_py_asgi_new_msg(req, nxt_py_http_disconnect_str); - if (nxt_slow_path(msg == NULL)) { - return; - } - - if (msg == Py_None) { - Py_DECREF(msg); - return; - } - - future = http->receive_future; - http->receive_future = NULL; - - res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_req_alert(req, "'set_result' call failed"); - nxt_python_print_exception(); - } - - Py_XDECREF(res); - Py_DECREF(future); - - Py_DECREF(msg); + nxt_py_asgi_http_emit_disconnect(http); } diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c index b80d10fa..87dcfaa2 100644 --- a/src/python/nxt_python_wsgi.c +++ b/src/python/nxt_python_wsgi.c @@ -51,7 +51,7 @@ typedef struct { } nxt_python_ctx_t; -static int nxt_python_wsgi_ctx_data_alloc(void **pdata); +static int nxt_python_wsgi_ctx_data_alloc(void **pdata, int main); static void nxt_python_wsgi_ctx_data_free(void *data); static int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx); static void nxt_python_wsgi_done(void); @@ -210,7 +210,7 @@ fail: static int -nxt_python_wsgi_ctx_data_alloc(void **pdata) +nxt_python_wsgi_ctx_data_alloc(void **pdata, int main) { nxt_python_ctx_t *pctx; diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index ca14af5b..522869b5 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -29,6 +29,11 @@ typedef struct { static nxt_int_t nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data); static VALUE nxt_ruby_init_basic(VALUE arg); + +static VALUE nxt_ruby_hook_procs_load(VALUE path); +static VALUE nxt_ruby_hook_register(VALUE arg); +static VALUE nxt_ruby_hook_call(VALUE name); + static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init); static VALUE nxt_ruby_require_rubygems(VALUE arg); @@ -78,6 +83,7 @@ static uint32_t compat[] = { NXT_VERNUM, NXT_DEBUG, }; +static VALUE nxt_ruby_hook_procs; static VALUE nxt_ruby_rackup; static VALUE nxt_ruby_call; @@ -115,6 +121,10 @@ static VALUE nxt_rb_server_addr_str; static VALUE nxt_rb_server_name_str; static VALUE nxt_rb_server_port_str; static VALUE nxt_rb_server_protocol_str; +static VALUE nxt_rb_on_worker_boot; +static VALUE nxt_rb_on_worker_shutdown; +static VALUE nxt_rb_on_thread_boot; +static VALUE nxt_rb_on_thread_shutdown; static nxt_ruby_string_t nxt_rb_strings[] = { { nxt_string("80"), &nxt_rb_80_str }, @@ -132,6 +142,10 @@ static nxt_ruby_string_t nxt_rb_strings[] = { { nxt_string("SERVER_NAME"), &nxt_rb_server_name_str }, { nxt_string("SERVER_PORT"), &nxt_rb_server_port_str }, { nxt_string("SERVER_PROTOCOL"), &nxt_rb_server_protocol_str }, + { nxt_string("on_worker_boot"), &nxt_rb_on_worker_boot }, + { nxt_string("on_worker_shutdown"), &nxt_rb_on_worker_shutdown }, + { nxt_string("on_thread_boot"), &nxt_rb_on_thread_boot }, + { nxt_string("on_thread_shutdown"), &nxt_rb_on_thread_shutdown }, { nxt_null_string, NULL }, }; @@ -183,11 +197,70 @@ nxt_ruby_done_strings(void) } +static VALUE +nxt_ruby_hook_procs_load(VALUE path) +{ + VALUE module, file, file_obj; + + module = rb_define_module("Unit"); + + nxt_ruby_hook_procs = rb_hash_new(); + + rb_gc_register_address(&nxt_ruby_hook_procs); + + rb_define_module_function(module, "on_worker_boot", + &nxt_ruby_hook_register, 0); + rb_define_module_function(module, "on_worker_shutdown", + &nxt_ruby_hook_register, 0); + rb_define_module_function(module, "on_thread_boot", + &nxt_ruby_hook_register, 0); + rb_define_module_function(module, "on_thread_shutdown", + &nxt_ruby_hook_register, 0); + + file = rb_const_get(rb_cObject, rb_intern("File")); + file_obj = rb_funcall(file, rb_intern("read"), 1, path); + + return rb_funcall(module, rb_intern("module_eval"), 3, file_obj, path, + INT2NUM(1)); +} + + +static VALUE +nxt_ruby_hook_register(VALUE arg) +{ + VALUE kernel, callee, callee_str; + + rb_need_block(); + + kernel = rb_const_get(rb_cObject, rb_intern("Kernel")); + callee = rb_funcall(kernel, rb_intern("__callee__"), 0); + callee_str = rb_funcall(callee, rb_intern("to_s"), 0); + + rb_hash_aset(nxt_ruby_hook_procs, callee_str, rb_block_proc()); + + return Qnil; +} + + +static VALUE +nxt_ruby_hook_call(VALUE name) +{ + VALUE proc; + + proc = rb_hash_lookup(nxt_ruby_hook_procs, name); + if (proc == Qnil) { + return Qnil; + } + + return rb_funcall(proc, rb_intern("call"), 0); +} + + static nxt_int_t nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) { int state, rc; - VALUE res; + VALUE res, path; nxt_ruby_ctx_t ruby_ctx; nxt_unit_ctx_t *unit_ctx; nxt_unit_init_t ruby_unit_init; @@ -231,6 +304,29 @@ nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) } nxt_ruby_call = Qnil; + nxt_ruby_hook_procs = Qnil; + + if (c->hooks.start != NULL) { + path = rb_str_new((const char *) c->hooks.start, + (long) c->hooks.length); + + rb_protect(nxt_ruby_hook_procs_load, path, &state); + rb_str_free(path); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, + "Failed to setup hooks"); + return NXT_ERROR; + } + } + + if (nxt_ruby_hook_procs != Qnil) { + rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_boot, &state); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + "Failed to call on_worker_boot()"); + return NXT_ERROR; + } + } nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init); if (nxt_slow_path(nxt_ruby_rackup == Qnil)) { @@ -274,11 +370,35 @@ nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) goto fail; } + if (nxt_ruby_hook_procs != Qnil) { + rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + "Failed to call on_thread_boot()"); + } + } + rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_unit_run, unit_ctx, nxt_ruby_ubf, unit_ctx); + if (nxt_ruby_hook_procs != Qnil) { + rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + "Failed to call on_thread_shutdown()"); + } + } + nxt_ruby_join_threads(unit_ctx, c); + if (nxt_ruby_hook_procs != Qnil) { + rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_shutdown, &state); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + "Failed to call on_worker_shutdown()"); + } + } + nxt_unit_done(unit_ctx); nxt_ruby_ctx_done(&ruby_ctx); @@ -1069,14 +1189,18 @@ nxt_ruby_exception_log(nxt_unit_request_info_t *req, uint32_t level, return; } + eclass = rb_class_name(rb_class_of(err)); + + msg = rb_funcall(err, rb_intern("message"), 0); ary = rb_funcall(err, rb_intern("backtrace"), 0); - if (nxt_slow_path(RARRAY_LEN(ary) == 0)) { + + if (RARRAY_LEN(ary) == 0) { + nxt_unit_req_log(req, level, "Ruby: %s (%s)", RSTRING_PTR(msg), + RSTRING_PTR(eclass)); + return; } - eclass = rb_class_name(rb_class_of(err)); - msg = rb_funcall(err, rb_intern("message"), 0); - nxt_unit_req_log(req, level, "Ruby: %s: %s (%s)", RSTRING_PTR(RARRAY_PTR(ary)[0]), RSTRING_PTR(msg), RSTRING_PTR(eclass)); @@ -1116,6 +1240,10 @@ nxt_ruby_atexit(void) rb_gc_unregister_address(&nxt_ruby_call); } + if (nxt_ruby_hook_procs != Qnil) { + rb_gc_unregister_address(&nxt_ruby_hook_procs); + } + nxt_ruby_done_strings(); ruby_cleanup(0); @@ -1178,6 +1306,7 @@ nxt_ruby_thread_create_gvl(void *rctx) static VALUE nxt_ruby_thread_func(VALUE arg) { + int state; nxt_unit_ctx_t *ctx; nxt_ruby_ctx_t *rctx; @@ -1190,9 +1319,25 @@ nxt_ruby_thread_func(VALUE arg) goto fail; } + if (nxt_ruby_hook_procs != Qnil) { + rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + "Failed to call on_thread_boot()"); + } + } + (void) rb_thread_call_without_gvl(nxt_ruby_unit_run, ctx, nxt_ruby_ubf, ctx); + if (nxt_ruby_hook_procs != Qnil) { + rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + "Failed to call on_thread_shutdown()"); + } + } + nxt_unit_done(ctx); fail: |