diff options
Diffstat (limited to 'nginx/ngx_http_js_module.c')
-rw-r--r-- | nginx/ngx_http_js_module.c | 399 |
1 files changed, 388 insertions, 11 deletions
diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index a4426f02..a1d54bdb 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -22,6 +22,27 @@ typedef struct { } ngx_http_js_loc_conf_t; +typedef struct { + ngx_http_conf_ctx_t *conf_ctx; + ngx_connection_t *connection; + void *padding; + + /** + * fd is used for event debug and should be at the same position + * as in ngx_connection_t: after a 3rd pointer. + */ + ngx_socket_t fd; + + ngx_str_t method; + ngx_msec_t interval; + ngx_msec_t jitter; + + ngx_log_t log; + ngx_http_log_ctx_t log_ctx; + ngx_event_t event; +} ngx_js_periodic_t; + + #define NJS_HEADER_SEMICOLON 0x1 #define NJS_HEADER_SINGLE 0x2 #define NJS_HEADER_ARRAY 0x4 @@ -45,6 +66,8 @@ typedef struct { ngx_chain_t **last_out; ngx_chain_t *free; ngx_chain_t *busy; + + ngx_js_periodic_t *periodic; } ngx_http_js_ctx_t; @@ -88,7 +111,8 @@ static ngx_int_t ngx_http_js_variable_set(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_js_variable_var(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); -static ngx_int_t ngx_http_js_init_vm(ngx_http_request_t *r); +static ngx_int_t ngx_http_js_init_vm(ngx_http_request_t *r, + unsigned inject_request); static void ngx_http_js_cleanup_ctx(void *data); static njs_int_t ngx_http_js_ext_keys_header(njs_vm_t *vm, njs_value_t *value, @@ -256,8 +280,18 @@ static size_t ngx_http_js_max_response_buffer_size(njs_vm_t *vm, static void ngx_http_js_handle_vm_event(ngx_http_request_t *r, njs_vm_event_t vm_event, njs_value_t *args, njs_uint_t nargs); +static void ngx_http_js_periodic_handler(ngx_event_t *ev); +static void ngx_http_js_periodic_write_event_handler(ngx_http_request_t *r); +static void ngx_http_js_periodic_shutdown_handler(ngx_event_t *ev); +static void ngx_http_js_periodic_finalize(ngx_http_request_t *r, ngx_int_t rc); +static void ngx_http_js_periodic_destroy(ngx_http_request_t *r, + ngx_js_periodic_t *periodic); + static njs_int_t ngx_js_http_init(njs_vm_t *vm); static ngx_int_t ngx_http_js_init(ngx_conf_t *cf); +static ngx_int_t ngx_http_js_init_worker(ngx_cycle_t *cycle); +static char *ngx_http_js_periodic(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_js_var(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_js_content(ngx_conf_t *cf, ngx_command_t *cmd, @@ -297,6 +331,13 @@ static ngx_command_t ngx_http_js_commands[] = { 0, NULL }, + { ngx_string("js_periodic"), + NGX_HTTP_LOC_CONF|NGX_CONF_ANY, + ngx_http_js_periodic, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("js_preload_object"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13, ngx_js_preload_object, @@ -439,7 +480,7 @@ ngx_module_t ngx_http_js_module = { NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ - NULL, /* init process */ + ngx_http_js_init_worker, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ @@ -863,7 +904,7 @@ ngx_http_js_content_event_handler(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js content event handler"); - rc = ngx_http_js_init_vm(r); + rc = ngx_http_js_init_vm(r, 1); if (rc == NGX_ERROR || rc == NGX_DECLINED) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); @@ -999,7 +1040,7 @@ ngx_http_js_header_filter(ngx_http_request_t *r) return ngx_http_next_header_filter(r); } - rc = ngx_http_js_init_vm(r); + rc = ngx_http_js_init_vm(r, 1); if (rc == NGX_ERROR || rc == NGX_DECLINED) { return NGX_ERROR; @@ -1051,7 +1092,7 @@ ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in) return ngx_http_next_body_filter(r, in); } - rc = ngx_http_js_init_vm(r); + rc = ngx_http_js_init_vm(r, 1); if (rc == NGX_ERROR || rc == NGX_DECLINED) { return NGX_ERROR; @@ -1165,7 +1206,7 @@ ngx_http_js_variable_set(ngx_http_request_t *r, ngx_http_variable_value_t *v, ngx_str_t value; ngx_http_js_ctx_t *ctx; - rc = ngx_http_js_init_vm(r); + rc = ngx_http_js_init_vm(r, 1); if (rc == NGX_ERROR) { return NGX_ERROR; @@ -1239,7 +1280,7 @@ ngx_http_js_variable_var(ngx_http_request_t *r, ngx_http_variable_value_t *v, static ngx_int_t -ngx_http_js_init_vm(ngx_http_request_t *r) +ngx_http_js_init_vm(ngx_http_request_t *r, unsigned inject_request) { njs_int_t rc; ngx_str_t exception; @@ -1318,10 +1359,12 @@ ngx_http_js_init_vm(ngx_http_request_t *r) return NGX_ERROR; } - rc = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->request), - ngx_http_js_request_proto_id, r, 0); - if (rc != NJS_OK) { - return NGX_ERROR; + if (inject_request) { + rc = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->request), + ngx_http_js_request_proto_id, r, 0); + if (rc != NJS_OK) { + return NGX_ERROR; + } } return NGX_OK; @@ -4048,6 +4091,221 @@ ngx_http_js_location(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, } +static void +ngx_http_js_periodic_handler(ngx_event_t *ev) +{ + ngx_int_t rc; + ngx_msec_t timer; + ngx_connection_t *c; + ngx_js_periodic_t *periodic; + ngx_http_js_ctx_t *ctx; + ngx_http_request_t *r; + ngx_http_connection_t hc; + + periodic = ev->data; + + timer = periodic->interval; + + if (periodic->jitter) { + timer += (ngx_msec_t) ngx_random() % periodic->jitter; + } + + ngx_add_timer(&periodic->event, timer); + + c = periodic->connection; + + if (c != NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http js periodic \"%V\" is already running, killing " + "previous instance", &periodic->method); + + ngx_http_js_periodic_finalize(c->data, NGX_ERROR); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, &periodic->log, 0, + "http js periodic handler: \"%V\"", &periodic->method); + + c = ngx_get_connection(0, &periodic->log); + + if (c == NULL) { + return; + } + + ngx_memzero(&hc, sizeof(ngx_http_connection_t)); + + hc.conf_ctx = periodic->conf_ctx; + + c->data = &hc; + + r = ngx_http_create_request(c); + + if (r == NULL) { + ngx_free_connection(c); + c->fd = (ngx_socket_t) -1; + return; + } + + c->data = r; + c->destroyed = 0; + c->pool = r->pool; + c->read->handler = ngx_http_js_periodic_shutdown_handler; + + periodic->connection = c; + periodic->log_ctx.request = r; + periodic->log_ctx.connection = c; + + r->method = NGX_HTTP_GET; + r->method_name = ngx_http_core_get_method; + + ngx_str_set(&r->uri, "/"); + r->unparsed_uri = r->uri; + r->valid_unparsed_uri = 1; + + r->health_check = 1; + r->write_event_handler = ngx_http_js_periodic_write_event_handler; + + rc = ngx_http_js_init_vm(r, 0); + + if (rc != NGX_OK) { + ngx_http_js_periodic_destroy(r, periodic); + return; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); + + ctx->periodic = periodic; + + r->count++; + + rc = ngx_js_invoke(ctx->vm, &periodic->method, &periodic->log, NULL, 0, + &ctx->retval); + + if (rc == NGX_AGAIN) { + rc = NGX_OK; + } + + r->count--; + + ngx_http_js_periodic_finalize(r, rc); +} + + +static void +ngx_http_js_periodic_write_event_handler(ngx_http_request_t *r) +{ + ngx_http_js_ctx_t *ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http js periodic write event handler"); + + ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); + + if (!njs_vm_pending(ctx->vm)) { + ngx_http_js_periodic_finalize(r, NGX_OK); + return; + } +} + + +static void +ngx_http_js_periodic_shutdown_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + + c = ev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http js periodic shutdown handler"); + + if (c->close) { + ngx_http_js_periodic_finalize(c->data, NGX_ERROR); + return; + } + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "http js periodic shutdown handler " + "while not closing"); +} + + +static void +ngx_http_js_periodic_finalize(ngx_http_request_t *r, ngx_int_t rc) +{ + ngx_http_js_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http js periodic finalize: \"%V\" rc: %i c: %i pending: %i", + &ctx->periodic->method, rc, r->count, + njs_vm_pending(ctx->vm)); + + if (r->count > 1 || (rc == NGX_OK && njs_vm_pending(ctx->vm))) { + return; + } + + ngx_http_js_periodic_destroy(r, ctx->periodic); +} + + +static void +ngx_http_js_periodic_destroy(ngx_http_request_t *r, ngx_js_periodic_t *periodic) +{ + ngx_connection_t *c; + ngx_http_cleanup_t *cln; + + c = r->connection; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http js periodic destroy: \"%V\"", + &periodic->method); + + periodic->connection = NULL; + + for (cln = r->cleanup; cln; cln = cln->next) { + if (cln->handler) { + cln->handler(cln->data); + } + } + + ngx_free_connection(c); + + c->fd = (ngx_socket_t) -1; + c->pool = NULL; + c->destroyed = 1; + + ngx_destroy_pool(r->pool); +} + + +static ngx_int_t +ngx_http_js_periodic_init(ngx_js_periodic_t *periodic) +{ + ngx_log_t *log; + ngx_msec_t jitter; + ngx_http_core_loc_conf_t *clcf; + + clcf = ngx_http_get_module_loc_conf(periodic->conf_ctx, + ngx_http_core_module); + log = clcf->error_log; + + ngx_memcpy(&periodic->log, log, sizeof(ngx_log_t)); + + periodic->log.data = &periodic->log_ctx; + periodic->connection = NULL; + + periodic->event.handler = ngx_http_js_periodic_handler; + periodic->event.data = periodic; + periodic->event.log = log; + periodic->event.cancelable = 1; + + jitter = periodic->jitter ? (ngx_msec_t) ngx_random() % periodic->jitter + : 0; + ngx_add_timer(&periodic->event, jitter + 1); + + return NGX_OK; +} + + static njs_host_event_t ngx_http_js_set_timer(njs_external_ptr_t external, uint64_t delay, njs_vm_event_t vm_event) @@ -4193,6 +4451,11 @@ ngx_http_js_handle_vm_event(ngx_http_request_t *r, njs_vm_event_t vm_event, ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "js exception: %V", &exception); + if (r->health_check) { + ngx_http_js_periodic_finalize(r, NGX_ERROR); + return; + } + ngx_http_finalize_request(r, NGX_ERROR); return; } @@ -4255,6 +4518,119 @@ ngx_http_js_init(ngx_conf_t *cf) } +static ngx_int_t +ngx_http_js_init_worker(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_js_periodic_t *periodics; + ngx_js_main_conf_t *jmcf; + + if ((ngx_process != NGX_PROCESS_WORKER) + && ngx_process != NGX_PROCESS_SINGLE) + { + return NGX_OK; + } + + jmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_js_module); + + if (jmcf == NULL || jmcf->periodics == NULL) { + return NGX_OK; + } + + periodics = jmcf->periodics->elts; + + for (i = 0; i < jmcf->periodics->nelts; i++) { + periodics[i].fd = 1000000 + i; + + if (ngx_http_js_periodic_init(&periodics[i]) != NGX_OK) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static char * +ngx_http_js_periodic(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_str_t *value, s; + ngx_msec_t interval, jitter; + ngx_uint_t i; + ngx_js_periodic_t *periodic; + ngx_js_main_conf_t *jmcf; + + if (cf->args->nelts < 2) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "method name is required"); + return NGX_CONF_ERROR; + } + + jmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_js_module); + + if (jmcf->periodics == NULL) { + jmcf->periodics = ngx_array_create(cf->pool, 1, + sizeof(ngx_js_periodic_t)); + if (jmcf->periodics == NULL) { + return NGX_CONF_ERROR; + } + } + + periodic = ngx_array_push(jmcf->periodics); + if (periodic == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(periodic, sizeof(ngx_js_periodic_t)); + + jitter = 0; + interval = 5000; + + value = cf->args->elts; + + for (i = 2; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "interval=", 9) == 0) { + s.len = value[i].len - 9; + s.data = value[i].data + 9; + + interval = ngx_parse_time(&s, 0); + + if (interval == (ngx_msec_t) NGX_ERROR || interval == 0) { + goto invalid; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "jitter=", 7) == 0) { + s.len = value[i].len - 7; + s.data = value[i].data + 7; + + jitter = ngx_parse_time(&s, 0); + + if (jitter == (ngx_msec_t) NGX_ERROR) { + goto invalid; + } + + continue; + } + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + periodic->method = value[1]; + periodic->interval = interval; + periodic->jitter = jitter; + periodic->conf_ctx = cf->ctx; + + return NGX_CONF_OK; +} + + static char * ngx_http_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -4429,6 +4805,7 @@ ngx_http_js_create_main_conf(ngx_conf_t *cf) * set by ngx_pcalloc(): * * jmcf->dicts = NULL; + * jmcf->periodics = NULL; */ return jmcf; |