comparison src/http/modules/ngx_http_limit_conn_module.c @ 7596:b45f052483b8

Limit conn: added shared context. Previously only an rbtree was associated with a limit_conn. To make it possible to associate more data with a limit_conn, shared context is introduced similar to limit_req. Also, shared pool pointer is kept in a way similar to limit_req.
author Roman Arutyunyan <arut@nginx.com>
date Mon, 18 Nov 2019 19:50:59 +0300
parents 9606d93aa586
children
comparison
equal deleted inserted replaced
7595:9606d93aa586 7596:b45f052483b8
14 #define NGX_HTTP_LIMIT_CONN_REJECTED 2 14 #define NGX_HTTP_LIMIT_CONN_REJECTED 2
15 #define NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN 3 15 #define NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN 3
16 16
17 17
18 typedef struct { 18 typedef struct {
19 u_char color; 19 u_char color;
20 u_char len; 20 u_char len;
21 u_short conn; 21 u_short conn;
22 u_char data[1]; 22 u_char data[1];
23 } ngx_http_limit_conn_node_t; 23 } ngx_http_limit_conn_node_t;
24 24
25 25
26 typedef struct { 26 typedef struct {
27 ngx_shm_zone_t *shm_zone; 27 ngx_shm_zone_t *shm_zone;
28 ngx_rbtree_node_t *node; 28 ngx_rbtree_node_t *node;
29 } ngx_http_limit_conn_cleanup_t; 29 } ngx_http_limit_conn_cleanup_t;
30 30
31 31
32 typedef struct { 32 typedef struct {
33 ngx_rbtree_t *rbtree; 33 ngx_rbtree_t rbtree;
34 ngx_http_complex_value_t key; 34 ngx_rbtree_node_t sentinel;
35 } ngx_http_limit_conn_shctx_t;
36
37
38 typedef struct {
39 ngx_http_limit_conn_shctx_t *sh;
40 ngx_slab_pool_t *shpool;
41 ngx_http_complex_value_t key;
35 } ngx_http_limit_conn_ctx_t; 42 } ngx_http_limit_conn_ctx_t;
36 43
37 44
38 typedef struct { 45 typedef struct {
39 ngx_shm_zone_t *shm_zone; 46 ngx_shm_zone_t *shm_zone;
40 ngx_uint_t conn; 47 ngx_uint_t conn;
41 } ngx_http_limit_conn_limit_t; 48 } ngx_http_limit_conn_limit_t;
42 49
43 50
44 typedef struct { 51 typedef struct {
45 ngx_array_t limits; 52 ngx_array_t limits;
46 ngx_uint_t log_level; 53 ngx_uint_t log_level;
47 ngx_uint_t status_code; 54 ngx_uint_t status_code;
48 ngx_flag_t dry_run; 55 ngx_flag_t dry_run;
49 } ngx_http_limit_conn_conf_t; 56 } ngx_http_limit_conn_conf_t;
50 57
51 58
52 static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, 59 static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree,
53 ngx_str_t *key, uint32_t hash); 60 ngx_str_t *key, uint32_t hash);
174 { 181 {
175 size_t n; 182 size_t n;
176 uint32_t hash; 183 uint32_t hash;
177 ngx_str_t key; 184 ngx_str_t key;
178 ngx_uint_t i; 185 ngx_uint_t i;
179 ngx_slab_pool_t *shpool;
180 ngx_rbtree_node_t *node; 186 ngx_rbtree_node_t *node;
181 ngx_pool_cleanup_t *cln; 187 ngx_pool_cleanup_t *cln;
182 ngx_http_limit_conn_ctx_t *ctx; 188 ngx_http_limit_conn_ctx_t *ctx;
183 ngx_http_limit_conn_node_t *lc; 189 ngx_http_limit_conn_node_t *lc;
184 ngx_http_limit_conn_conf_t *lccf; 190 ngx_http_limit_conn_conf_t *lccf;
213 219
214 r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_PASSED; 220 r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_PASSED;
215 221
216 hash = ngx_crc32_short(key.data, key.len); 222 hash = ngx_crc32_short(key.data, key.len);
217 223
218 shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr; 224 ngx_shmtx_lock(&ctx->shpool->mutex);
219 225
220 ngx_shmtx_lock(&shpool->mutex); 226 node = ngx_http_limit_conn_lookup(&ctx->sh->rbtree, &key, hash);
221
222 node = ngx_http_limit_conn_lookup(ctx->rbtree, &key, hash);
223 227
224 if (node == NULL) { 228 if (node == NULL) {
225 229
226 n = offsetof(ngx_rbtree_node_t, color) 230 n = offsetof(ngx_rbtree_node_t, color)
227 + offsetof(ngx_http_limit_conn_node_t, data) 231 + offsetof(ngx_http_limit_conn_node_t, data)
228 + key.len; 232 + key.len;
229 233
230 node = ngx_slab_alloc_locked(shpool, n); 234 node = ngx_slab_alloc_locked(ctx->shpool, n);
231 235
232 if (node == NULL) { 236 if (node == NULL) {
233 ngx_shmtx_unlock(&shpool->mutex); 237 ngx_shmtx_unlock(&ctx->shpool->mutex);
234 ngx_http_limit_conn_cleanup_all(r->pool); 238 ngx_http_limit_conn_cleanup_all(r->pool);
235 239
236 if (lccf->dry_run) { 240 if (lccf->dry_run) {
237 r->main->limit_conn_status = 241 r->main->limit_conn_status =
238 NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN; 242 NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN;
249 node->key = hash; 253 node->key = hash;
250 lc->len = (u_char) key.len; 254 lc->len = (u_char) key.len;
251 lc->conn = 1; 255 lc->conn = 1;
252 ngx_memcpy(lc->data, key.data, key.len); 256 ngx_memcpy(lc->data, key.data, key.len);
253 257
254 ngx_rbtree_insert(ctx->rbtree, node); 258 ngx_rbtree_insert(&ctx->sh->rbtree, node);
255 259
256 } else { 260 } else {
257 261
258 lc = (ngx_http_limit_conn_node_t *) &node->color; 262 lc = (ngx_http_limit_conn_node_t *) &node->color;
259 263
260 if ((ngx_uint_t) lc->conn >= limits[i].conn) { 264 if ((ngx_uint_t) lc->conn >= limits[i].conn) {
261 265
262 ngx_shmtx_unlock(&shpool->mutex); 266 ngx_shmtx_unlock(&ctx->shpool->mutex);
263 267
264 ngx_log_error(lccf->log_level, r->connection->log, 0, 268 ngx_log_error(lccf->log_level, r->connection->log, 0,
265 "limiting connections%s by zone \"%V\"", 269 "limiting connections%s by zone \"%V\"",
266 lccf->dry_run ? ", dry run," : "", 270 lccf->dry_run ? ", dry run," : "",
267 &limits[i].shm_zone->shm.name); 271 &limits[i].shm_zone->shm.name);
283 } 287 }
284 288
285 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 289 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
286 "limit conn: %08Xi %d", node->key, lc->conn); 290 "limit conn: %08Xi %d", node->key, lc->conn);
287 291
288 ngx_shmtx_unlock(&shpool->mutex); 292 ngx_shmtx_unlock(&ctx->shpool->mutex);
289 293
290 cln = ngx_pool_cleanup_add(r->pool, 294 cln = ngx_pool_cleanup_add(r->pool,
291 sizeof(ngx_http_limit_conn_cleanup_t)); 295 sizeof(ngx_http_limit_conn_cleanup_t));
292 if (cln == NULL) { 296 if (cln == NULL) {
293 return NGX_HTTP_INTERNAL_SERVER_ERROR; 297 return NGX_HTTP_INTERNAL_SERVER_ERROR;
387 static void 391 static void
388 ngx_http_limit_conn_cleanup(void *data) 392 ngx_http_limit_conn_cleanup(void *data)
389 { 393 {
390 ngx_http_limit_conn_cleanup_t *lccln = data; 394 ngx_http_limit_conn_cleanup_t *lccln = data;
391 395
392 ngx_slab_pool_t *shpool;
393 ngx_rbtree_node_t *node; 396 ngx_rbtree_node_t *node;
394 ngx_http_limit_conn_ctx_t *ctx; 397 ngx_http_limit_conn_ctx_t *ctx;
395 ngx_http_limit_conn_node_t *lc; 398 ngx_http_limit_conn_node_t *lc;
396 399
397 ctx = lccln->shm_zone->data; 400 ctx = lccln->shm_zone->data;
398 shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr;
399 node = lccln->node; 401 node = lccln->node;
400 lc = (ngx_http_limit_conn_node_t *) &node->color; 402 lc = (ngx_http_limit_conn_node_t *) &node->color;
401 403
402 ngx_shmtx_lock(&shpool->mutex); 404 ngx_shmtx_lock(&ctx->shpool->mutex);
403 405
404 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0, 406 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0,
405 "limit conn cleanup: %08Xi %d", node->key, lc->conn); 407 "limit conn cleanup: %08Xi %d", node->key, lc->conn);
406 408
407 lc->conn--; 409 lc->conn--;
408 410
409 if (lc->conn == 0) { 411 if (lc->conn == 0) {
410 ngx_rbtree_delete(ctx->rbtree, node); 412 ngx_rbtree_delete(&ctx->sh->rbtree, node);
411 ngx_slab_free_locked(shpool, node); 413 ngx_slab_free_locked(ctx->shpool, node);
412 } 414 }
413 415
414 ngx_shmtx_unlock(&shpool->mutex); 416 ngx_shmtx_unlock(&ctx->shpool->mutex);
415 } 417 }
416 418
417 419
418 static ngx_inline void 420 static ngx_inline void
419 ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool) 421 ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool)
435 ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data) 437 ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
436 { 438 {
437 ngx_http_limit_conn_ctx_t *octx = data; 439 ngx_http_limit_conn_ctx_t *octx = data;
438 440
439 size_t len; 441 size_t len;
440 ngx_slab_pool_t *shpool;
441 ngx_rbtree_node_t *sentinel;
442 ngx_http_limit_conn_ctx_t *ctx; 442 ngx_http_limit_conn_ctx_t *ctx;
443 443
444 ctx = shm_zone->data; 444 ctx = shm_zone->data;
445 445
446 if (octx) { 446 if (octx) {
455 &shm_zone->shm.name, &ctx->key.value, 455 &shm_zone->shm.name, &ctx->key.value,
456 &octx->key.value); 456 &octx->key.value);
457 return NGX_ERROR; 457 return NGX_ERROR;
458 } 458 }
459 459
460 ctx->rbtree = octx->rbtree; 460 ctx->sh = octx->sh;
461 ctx->shpool = octx->shpool;
461 462
462 return NGX_OK; 463 return NGX_OK;
463 } 464 }
464 465
465 shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; 466 ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
466 467
467 if (shm_zone->shm.exists) { 468 if (shm_zone->shm.exists) {
468 ctx->rbtree = shpool->data; 469 ctx->sh = ctx->shpool->data;
469 470
470 return NGX_OK; 471 return NGX_OK;
471 } 472 }
472 473
473 ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); 474 ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_conn_shctx_t));
474 if (ctx->rbtree == NULL) { 475 if (ctx->sh == NULL) {
475 return NGX_ERROR; 476 return NGX_ERROR;
476 } 477 }
477 478
478 shpool->data = ctx->rbtree; 479 ctx->shpool->data = ctx->sh;
479 480
480 sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); 481 ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
481 if (sentinel == NULL) { 482 ngx_http_limit_conn_rbtree_insert_value);
483
484 len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len;
485
486 ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
487 if (ctx->shpool->log_ctx == NULL) {
482 return NGX_ERROR; 488 return NGX_ERROR;
483 } 489 }
484 490
485 ngx_rbtree_init(ctx->rbtree, sentinel, 491 ngx_sprintf(ctx->shpool->log_ctx, " in limit_conn_zone \"%V\"%Z",
486 ngx_http_limit_conn_rbtree_insert_value);
487
488 len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len;
489
490 shpool->log_ctx = ngx_slab_alloc(shpool, len);
491 if (shpool->log_ctx == NULL) {
492 return NGX_ERROR;
493 }
494
495 ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z",
496 &shm_zone->shm.name); 492 &shm_zone->shm.name);
497 493
498 return NGX_OK; 494 return NGX_OK;
499 } 495 }
500 496