diff --git a/src/http/modules/ngx_http_limit_conn_module.c b/src/http/modules/ngx_http_limit_conn_module.c index 913d599..4e3e7d5 100644 --- a/src/http/modules/ngx_http_limit_conn_module.c +++ b/src/http/modules/ngx_http_limit_conn_module.c @@ -10,36 +10,49 @@ #include +#define NGX_HTTP_LIMIT_CONN_PASSED 1 +#define NGX_HTTP_LIMIT_CONN_REJECTED 2 +#define NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN 3 + + typedef struct { - u_char color; - u_char len; - u_short conn; - u_char data[1]; + u_char color; + u_char len; + u_short conn; + u_char data[1]; } ngx_http_limit_conn_node_t; typedef struct { - ngx_shm_zone_t *shm_zone; - ngx_rbtree_node_t *node; + ngx_shm_zone_t *shm_zone; + ngx_rbtree_node_t *node; } ngx_http_limit_conn_cleanup_t; typedef struct { - ngx_rbtree_t *rbtree; - ngx_http_complex_value_t key; + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; +} ngx_http_limit_conn_shctx_t; + + +typedef struct { + ngx_http_limit_conn_shctx_t *sh; + ngx_slab_pool_t *shpool; + ngx_http_complex_value_t key; } ngx_http_limit_conn_ctx_t; typedef struct { - ngx_shm_zone_t *shm_zone; - ngx_uint_t conn; + ngx_shm_zone_t *shm_zone; + ngx_uint_t conn; } ngx_http_limit_conn_limit_t; typedef struct { - ngx_array_t limits; - ngx_uint_t log_level; - ngx_uint_t status_code; + ngx_array_t limits; + ngx_uint_t log_level; + ngx_uint_t status_code; + ngx_flag_t dry_run; } ngx_http_limit_conn_conf_t; @@ -48,6 +61,8 @@ static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, static void ngx_http_limit_conn_cleanup(void *data); static ngx_inline void ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool); +static ngx_int_t ngx_http_limit_conn_status_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf); static char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child); @@ -55,6 +70,7 @@ static char *ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_http_limit_conn_add_variables(ngx_conf_t *cf); static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf); @@ -102,12 +118,19 @@ static ngx_command_t ngx_http_limit_conn_commands[] = { offsetof(ngx_http_limit_conn_conf_t, status_code), &ngx_http_limit_conn_status_bounds }, + { ngx_string("limit_conn_dry_run"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_limit_conn_conf_t, dry_run), + NULL }, + ngx_null_command }; static ngx_http_module_t ngx_http_limit_conn_module_ctx = { - NULL, /* preconfiguration */ + ngx_http_limit_conn_add_variables, /* preconfiguration */ ngx_http_limit_conn_init, /* postconfiguration */ NULL, /* create main configuration */ @@ -137,6 +160,22 @@ ngx_module_t ngx_http_limit_conn_module = { }; +static ngx_http_variable_t ngx_http_limit_conn_vars[] = { + + { ngx_string("limit_conn_status"), NULL, + ngx_http_limit_conn_status_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + ngx_http_null_variable +}; + + +static ngx_str_t ngx_http_limit_conn_status[] = { + ngx_string("PASSED"), + ngx_string("REJECTED"), + ngx_string("REJECTED_DRY_RUN") +}; + + static ngx_int_t ngx_http_limit_conn_handler(ngx_http_request_t *r) { @@ -144,7 +183,6 @@ ngx_http_limit_conn_handler(ngx_http_request_t *r) uint32_t hash; ngx_str_t key; ngx_uint_t i; - ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node; ngx_pool_cleanup_t *cln; ngx_http_limit_conn_ctx_t *ctx; @@ -153,7 +191,7 @@ ngx_http_limit_conn_handler(ngx_http_request_t *r) ngx_http_limit_conn_limit_t *limits; ngx_http_limit_conn_cleanup_t *lccln; - if (r->main->limit_conn_set) { + if (r->main->limit_conn_status) { return NGX_DECLINED; } @@ -179,15 +217,13 @@ ngx_http_limit_conn_handler(ngx_http_request_t *r) continue; } - r->main->limit_conn_set = 1; + r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_PASSED; hash = ngx_crc32_short(key.data, key.len); - shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr; + ngx_shmtx_lock(&ctx->shpool->mutex); - ngx_shmtx_lock(&shpool->mutex); - - node = ngx_http_limit_conn_lookup(ctx->rbtree, &key, hash); + node = ngx_http_limit_conn_lookup(&ctx->sh->rbtree, &key, hash); if (node == NULL) { @@ -195,11 +231,20 @@ ngx_http_limit_conn_handler(ngx_http_request_t *r) + offsetof(ngx_http_limit_conn_node_t, data) + key.len; - node = ngx_slab_alloc_locked(shpool, n); + node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_http_limit_conn_cleanup_all(r->pool); + + if (lccf->dry_run) { + r->main->limit_conn_status = + NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN; + return NGX_DECLINED; + } + + r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_REJECTED; + return lccf->status_code; } @@ -210,7 +255,7 @@ ngx_http_limit_conn_handler(ngx_http_request_t *r) lc->conn = 1; ngx_memcpy(lc->data, key.data, key.len); - ngx_rbtree_insert(ctx->rbtree, node); + ngx_rbtree_insert(&ctx->sh->rbtree, node); } else { @@ -218,13 +263,23 @@ ngx_http_limit_conn_handler(ngx_http_request_t *r) if ((ngx_uint_t) lc->conn >= limits[i].conn) { - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(lccf->log_level, r->connection->log, 0, - "limiting connections by zone \"%V\"", + "limiting connections%s by zone \"%V\"", + lccf->dry_run ? ", dry run," : "", &limits[i].shm_zone->shm.name); ngx_http_limit_conn_cleanup_all(r->pool); + + if (lccf->dry_run) { + r->main->limit_conn_status = + NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN; + return NGX_DECLINED; + } + + r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_REJECTED; + return lccf->status_code; } @@ -234,7 +289,7 @@ ngx_http_limit_conn_handler(ngx_http_request_t *r) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit conn: %08Xi %d", node->key, lc->conn); - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_conn_cleanup_t)); @@ -338,17 +393,15 @@ ngx_http_limit_conn_cleanup(void *data) { ngx_http_limit_conn_cleanup_t *lccln = data; - ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node; ngx_http_limit_conn_ctx_t *ctx; ngx_http_limit_conn_node_t *lc; ctx = lccln->shm_zone->data; - shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr; node = lccln->node; lc = (ngx_http_limit_conn_node_t *) &node->color; - ngx_shmtx_lock(&shpool->mutex); + ngx_shmtx_lock(&ctx->shpool->mutex); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0, "limit conn cleanup: %08Xi %d", node->key, lc->conn); @@ -356,11 +409,11 @@ ngx_http_limit_conn_cleanup(void *data) lc->conn--; if (lc->conn == 0) { - ngx_rbtree_delete(ctx->rbtree, node); - ngx_slab_free_locked(shpool, node); + ngx_rbtree_delete(&ctx->sh->rbtree, node); + ngx_slab_free_locked(ctx->shpool, node); } - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); } @@ -386,8 +439,6 @@ ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data) ngx_http_limit_conn_ctx_t *octx = data; size_t len; - ngx_slab_pool_t *shpool; - ngx_rbtree_node_t *sentinel; ngx_http_limit_conn_ctx_t *ctx; ctx = shm_zone->data; @@ -406,48 +457,63 @@ ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data) return NGX_ERROR; } - ctx->rbtree = octx->rbtree; + ctx->sh = octx->sh; + ctx->shpool = octx->shpool; return NGX_OK; } - shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; if (shm_zone->shm.exists) { - ctx->rbtree = shpool->data; + ctx->sh = ctx->shpool->data; return NGX_OK; } - ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); - if (ctx->rbtree == NULL) { + ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_conn_shctx_t)); + if (ctx->sh == NULL) { return NGX_ERROR; } - shpool->data = ctx->rbtree; + ctx->shpool->data = ctx->sh; - sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); - if (sentinel == NULL) { - return NGX_ERROR; - } - - ngx_rbtree_init(ctx->rbtree, sentinel, + ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel, ngx_http_limit_conn_rbtree_insert_value); len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len; - shpool->log_ctx = ngx_slab_alloc(shpool, len); - if (shpool->log_ctx == NULL) { + ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len); + if (ctx->shpool->log_ctx == NULL) { return NGX_ERROR; } - ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", + ngx_sprintf(ctx->shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", &shm_zone->shm.name); return NGX_OK; } +static ngx_int_t +ngx_http_limit_conn_status_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->main->limit_conn_status == 0) { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = ngx_http_limit_conn_status[r->main->limit_conn_status - 1].len; + v->data = ngx_http_limit_conn_status[r->main->limit_conn_status - 1].data; + + return NGX_OK; +} + + static void * ngx_http_limit_conn_create_conf(ngx_conf_t *cf) { @@ -466,6 +532,7 @@ ngx_http_limit_conn_create_conf(ngx_conf_t *cf) conf->log_level = NGX_CONF_UNSET_UINT; conf->status_code = NGX_CONF_UNSET_UINT; + conf->dry_run = NGX_CONF_UNSET; return conf; } @@ -485,6 +552,8 @@ ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->status_code, prev->status_code, NGX_HTTP_SERVICE_UNAVAILABLE); + ngx_conf_merge_value(conf->dry_run, prev->dry_run, 0); + return NGX_CONF_OK; } @@ -651,6 +720,25 @@ ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } +static ngx_int_t +ngx_http_limit_conn_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var, *v; + + for (v = ngx_http_limit_conn_vars; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf) { diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index d19a389..70c2d42 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -510,9 +510,9 @@ struct ngx_http_request_s { /* * instead of using the request context data in * ngx_http_limit_conn_module and ngx_http_limit_req_module - * we use the single bits in the request structure + * we use the bit fields in the request structure */ - unsigned limit_conn_set:1; + unsigned limit_conn_status:2; unsigned limit_req_status:3; unsigned limit_rate_set:1; diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h index 57e73e0..9e35832 100644 --- a/src/stream/ngx_stream.h +++ b/src/stream/ngx_stream.h @@ -226,6 +226,8 @@ struct ngx_stream_session_s { unsigned stat_processing:1; unsigned health_check:1; + + unsigned limit_conn_status:2; }; diff --git a/src/stream/ngx_stream_limit_conn_module.c b/src/stream/ngx_stream_limit_conn_module.c index b64a426..bae034a 100644 --- a/src/stream/ngx_stream_limit_conn_module.c +++ b/src/stream/ngx_stream_limit_conn_module.c @@ -10,35 +10,48 @@ #include +#define NGX_STREAM_LIMIT_CONN_PASSED 1 +#define NGX_STREAM_LIMIT_CONN_REJECTED 2 +#define NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN 3 + + typedef struct { - u_char color; - u_char len; - u_short conn; - u_char data[1]; + u_char color; + u_char len; + u_short conn; + u_char data[1]; } ngx_stream_limit_conn_node_t; typedef struct { - ngx_shm_zone_t *shm_zone; - ngx_rbtree_node_t *node; + ngx_shm_zone_t *shm_zone; + ngx_rbtree_node_t *node; } ngx_stream_limit_conn_cleanup_t; typedef struct { - ngx_rbtree_t *rbtree; - ngx_stream_complex_value_t key; + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; +} ngx_stream_limit_conn_shctx_t; + + +typedef struct { + ngx_stream_limit_conn_shctx_t *sh; + ngx_slab_pool_t *shpool; + ngx_stream_complex_value_t key; } ngx_stream_limit_conn_ctx_t; typedef struct { - ngx_shm_zone_t *shm_zone; - ngx_uint_t conn; + ngx_shm_zone_t *shm_zone; + ngx_uint_t conn; } ngx_stream_limit_conn_limit_t; typedef struct { - ngx_array_t limits; - ngx_uint_t log_level; + ngx_array_t limits; + ngx_uint_t log_level; + ngx_flag_t dry_run; } ngx_stream_limit_conn_conf_t; @@ -47,6 +60,8 @@ static ngx_rbtree_node_t *ngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree, static void ngx_stream_limit_conn_cleanup(void *data); static ngx_inline void ngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool); +static ngx_int_t ngx_stream_limit_conn_status_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); static void *ngx_stream_limit_conn_create_conf(ngx_conf_t *cf); static char *ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child); @@ -54,6 +69,7 @@ static char *ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_stream_limit_conn_add_variables(ngx_conf_t *cf); static ngx_int_t ngx_stream_limit_conn_init(ngx_conf_t *cf); @@ -89,12 +105,19 @@ static ngx_command_t ngx_stream_limit_conn_commands[] = { offsetof(ngx_stream_limit_conn_conf_t, log_level), &ngx_stream_limit_conn_log_levels }, + { ngx_string("limit_conn_dry_run"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_limit_conn_conf_t, dry_run), + NULL }, + ngx_null_command }; static ngx_stream_module_t ngx_stream_limit_conn_module_ctx = { - NULL, /* preconfiguration */ + ngx_stream_limit_conn_add_variables, /* preconfiguration */ ngx_stream_limit_conn_init, /* postconfiguration */ NULL, /* create main configuration */ @@ -121,6 +144,22 @@ ngx_module_t ngx_stream_limit_conn_module = { }; +static ngx_stream_variable_t ngx_stream_limit_conn_vars[] = { + + { ngx_string("limit_conn_status"), NULL, + ngx_stream_limit_conn_status_variable, 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + ngx_stream_null_variable +}; + + +static ngx_str_t ngx_stream_limit_conn_status[] = { + ngx_string("PASSED"), + ngx_string("REJECTED"), + ngx_string("REJECTED_DRY_RUN") +}; + + static ngx_int_t ngx_stream_limit_conn_handler(ngx_stream_session_t *s) { @@ -128,7 +167,6 @@ ngx_stream_limit_conn_handler(ngx_stream_session_t *s) uint32_t hash; ngx_str_t key; ngx_uint_t i; - ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node; ngx_pool_cleanup_t *cln; ngx_stream_limit_conn_ctx_t *ctx; @@ -159,13 +197,13 @@ ngx_stream_limit_conn_handler(ngx_stream_session_t *s) continue; } + s->limit_conn_status = NGX_STREAM_LIMIT_CONN_PASSED; + hash = ngx_crc32_short(key.data, key.len); - shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr; + ngx_shmtx_lock(&ctx->shpool->mutex); - ngx_shmtx_lock(&shpool->mutex); - - node = ngx_stream_limit_conn_lookup(ctx->rbtree, &key, hash); + node = ngx_stream_limit_conn_lookup(&ctx->sh->rbtree, &key, hash); if (node == NULL) { @@ -173,11 +211,20 @@ ngx_stream_limit_conn_handler(ngx_stream_session_t *s) + offsetof(ngx_stream_limit_conn_node_t, data) + key.len; - node = ngx_slab_alloc_locked(shpool, n); + node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_stream_limit_conn_cleanup_all(s->connection->pool); + + if (lccf->dry_run) { + s->limit_conn_status = + NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN; + return NGX_DECLINED; + } + + s->limit_conn_status = NGX_STREAM_LIMIT_CONN_REJECTED; + return NGX_STREAM_SERVICE_UNAVAILABLE; } @@ -188,7 +235,7 @@ ngx_stream_limit_conn_handler(ngx_stream_session_t *s) lc->conn = 1; ngx_memcpy(lc->data, key.data, key.len); - ngx_rbtree_insert(ctx->rbtree, node); + ngx_rbtree_insert(&ctx->sh->rbtree, node); } else { @@ -196,13 +243,23 @@ ngx_stream_limit_conn_handler(ngx_stream_session_t *s) if ((ngx_uint_t) lc->conn >= limits[i].conn) { - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(lccf->log_level, s->connection->log, 0, - "limiting connections by zone \"%V\"", + "limiting connections%s by zone \"%V\"", + lccf->dry_run ? ", dry run," : "", &limits[i].shm_zone->shm.name); ngx_stream_limit_conn_cleanup_all(s->connection->pool); + + if (lccf->dry_run) { + s->limit_conn_status = + NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN; + return NGX_DECLINED; + } + + s->limit_conn_status = NGX_STREAM_LIMIT_CONN_REJECTED; + return NGX_STREAM_SERVICE_UNAVAILABLE; } @@ -212,7 +269,7 @@ ngx_stream_limit_conn_handler(ngx_stream_session_t *s) ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "limit conn: %08Xi %d", node->key, lc->conn); - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); cln = ngx_pool_cleanup_add(s->connection->pool, sizeof(ngx_stream_limit_conn_cleanup_t)); @@ -317,17 +374,15 @@ ngx_stream_limit_conn_cleanup(void *data) { ngx_stream_limit_conn_cleanup_t *lccln = data; - ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node; ngx_stream_limit_conn_ctx_t *ctx; ngx_stream_limit_conn_node_t *lc; ctx = lccln->shm_zone->data; - shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr; node = lccln->node; lc = (ngx_stream_limit_conn_node_t *) &node->color; - ngx_shmtx_lock(&shpool->mutex); + ngx_shmtx_lock(&ctx->shpool->mutex); ngx_log_debug2(NGX_LOG_DEBUG_STREAM, lccln->shm_zone->shm.log, 0, "limit conn cleanup: %08Xi %d", node->key, lc->conn); @@ -335,11 +390,11 @@ ngx_stream_limit_conn_cleanup(void *data) lc->conn--; if (lc->conn == 0) { - ngx_rbtree_delete(ctx->rbtree, node); - ngx_slab_free_locked(shpool, node); + ngx_rbtree_delete(&ctx->sh->rbtree, node); + ngx_slab_free_locked(ctx->shpool, node); } - ngx_shmtx_unlock(&shpool->mutex); + ngx_shmtx_unlock(&ctx->shpool->mutex); } @@ -365,8 +420,6 @@ ngx_stream_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data) ngx_stream_limit_conn_ctx_t *octx = data; size_t len; - ngx_slab_pool_t *shpool; - ngx_rbtree_node_t *sentinel; ngx_stream_limit_conn_ctx_t *ctx; ctx = shm_zone->data; @@ -385,48 +438,64 @@ ngx_stream_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data) return NGX_ERROR; } - ctx->rbtree = octx->rbtree; + ctx->sh = octx->sh; + ctx->shpool = octx->shpool; return NGX_OK; } - shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; if (shm_zone->shm.exists) { - ctx->rbtree = shpool->data; + ctx->sh = ctx->shpool->data; return NGX_OK; } - ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); - if (ctx->rbtree == NULL) { + ctx->sh = ngx_slab_alloc(ctx->shpool, + sizeof(ngx_stream_limit_conn_shctx_t)); + if (ctx->sh == NULL) { return NGX_ERROR; } - shpool->data = ctx->rbtree; + ctx->shpool->data = ctx->sh; - sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); - if (sentinel == NULL) { - return NGX_ERROR; - } - - ngx_rbtree_init(ctx->rbtree, sentinel, + ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel, ngx_stream_limit_conn_rbtree_insert_value); len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len; - shpool->log_ctx = ngx_slab_alloc(shpool, len); - if (shpool->log_ctx == NULL) { + ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len); + if (ctx->shpool->log_ctx == NULL) { return NGX_ERROR; } - ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", + ngx_sprintf(ctx->shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", &shm_zone->shm.name); return NGX_OK; } +static ngx_int_t +ngx_stream_limit_conn_status_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + if (s->limit_conn_status == 0) { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = ngx_stream_limit_conn_status[s->limit_conn_status - 1].len; + v->data = ngx_stream_limit_conn_status[s->limit_conn_status - 1].data; + + return NGX_OK; +} + + static void * ngx_stream_limit_conn_create_conf(ngx_conf_t *cf) { @@ -444,6 +513,7 @@ ngx_stream_limit_conn_create_conf(ngx_conf_t *cf) */ conf->log_level = NGX_CONF_UNSET_UINT; + conf->dry_run = NGX_CONF_UNSET; return conf; } @@ -461,6 +531,8 @@ ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR); + ngx_conf_merge_value(conf->dry_run, prev->dry_run, 0); + return NGX_CONF_OK; } @@ -627,6 +699,25 @@ ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } +static ngx_int_t +ngx_stream_limit_conn_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_limit_conn_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + static ngx_int_t ngx_stream_limit_conn_init(ngx_conf_t *cf) {