comparison src/http/modules/ngx_http_limit_zone_module.c @ 4271:b86cceba426a

Limit zone: support for multiple "limit_conn" limits.
author Valentin Bartenev <vbart@nginx.com>
date Thu, 10 Nov 2011 16:08:13 +0000
parents 3544987fef85
children 260d591cb6a3
comparison
equal deleted inserted replaced
4270:3544987fef85 4271:b86cceba426a
31 31
32 32
33 typedef struct { 33 typedef struct {
34 ngx_shm_zone_t *shm_zone; 34 ngx_shm_zone_t *shm_zone;
35 ngx_uint_t conn; 35 ngx_uint_t conn;
36 } ngx_http_limit_zone_limit_t;
37
38
39 typedef struct {
40 ngx_array_t limits;
36 ngx_uint_t log_level; 41 ngx_uint_t log_level;
37 } ngx_http_limit_zone_conf_t; 42 } ngx_http_limit_zone_conf_t;
38 43
39 44
40 static ngx_rbtree_node_t *ngx_http_limit_zone_lookup(ngx_rbtree_t *rbtree, 45 static ngx_rbtree_node_t *ngx_http_limit_zone_lookup(ngx_rbtree_t *rbtree,
41 ngx_http_variable_value_t *vv, uint32_t hash); 46 ngx_http_variable_value_t *vv, uint32_t hash);
42 static void ngx_http_limit_zone_cleanup(void *data); 47 static void ngx_http_limit_zone_cleanup(void *data);
48 static ngx_inline void ngx_http_limit_zone_cleanup_all(ngx_pool_t *pool);
43 49
44 static void *ngx_http_limit_zone_create_conf(ngx_conf_t *cf); 50 static void *ngx_http_limit_zone_create_conf(ngx_conf_t *cf);
45 static char *ngx_http_limit_zone_merge_conf(ngx_conf_t *cf, void *parent, 51 static char *ngx_http_limit_zone_merge_conf(ngx_conf_t *cf, void *parent,
46 void *child); 52 void *child);
47 static char *ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd, 53 static char *ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd,
121 static ngx_int_t 127 static ngx_int_t
122 ngx_http_limit_zone_handler(ngx_http_request_t *r) 128 ngx_http_limit_zone_handler(ngx_http_request_t *r)
123 { 129 {
124 size_t len, n; 130 size_t len, n;
125 uint32_t hash; 131 uint32_t hash;
132 ngx_uint_t i;
126 ngx_slab_pool_t *shpool; 133 ngx_slab_pool_t *shpool;
127 ngx_rbtree_node_t *node; 134 ngx_rbtree_node_t *node;
128 ngx_pool_cleanup_t *cln; 135 ngx_pool_cleanup_t *cln;
129 ngx_http_variable_value_t *vv; 136 ngx_http_variable_value_t *vv;
130 ngx_http_limit_zone_ctx_t *ctx; 137 ngx_http_limit_zone_ctx_t *ctx;
131 ngx_http_limit_zone_node_t *lz; 138 ngx_http_limit_zone_node_t *lz;
132 ngx_http_limit_zone_conf_t *lzcf; 139 ngx_http_limit_zone_conf_t *lzcf;
140 ngx_http_limit_zone_limit_t *limits;
133 ngx_http_limit_zone_cleanup_t *lzcln; 141 ngx_http_limit_zone_cleanup_t *lzcln;
134 142
135 if (r->main->limit_zone_set) { 143 if (r->main->limit_zone_set) {
136 return NGX_DECLINED; 144 return NGX_DECLINED;
137 } 145 }
138 146
139 lzcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_zone_module); 147 lzcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_zone_module);
140 148 limits = lzcf->limits.elts;
141 if (lzcf->shm_zone == NULL) {
142 return NGX_DECLINED;
143 }
144
145 ctx = lzcf->shm_zone->data;
146
147 vv = ngx_http_get_indexed_variable(r, ctx->index);
148
149 if (vv == NULL || vv->not_found) {
150 return NGX_DECLINED;
151 }
152
153 len = vv->len;
154
155 if (len == 0) {
156 return NGX_DECLINED;
157 }
158
159 if (len > 255) {
160 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
161 "the value of the \"%V\" variable "
162 "is more than 255 bytes: \"%v\"",
163 &ctx->var, vv);
164 return NGX_DECLINED;
165 }
166 149
167 r->main->limit_zone_set = 1; 150 r->main->limit_zone_set = 1;
168 151
169 hash = ngx_crc32_short(vv->data, len); 152 for (i = 0; i < lzcf->limits.nelts; i++) {
170 153 ctx = limits[i].shm_zone->data;
171 cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_zone_cleanup_t)); 154
172 if (cln == NULL) { 155 vv = ngx_http_get_indexed_variable(r, ctx->index);
173 return NGX_HTTP_INTERNAL_SERVER_ERROR; 156
174 } 157 if (vv == NULL || vv->not_found) {
175 158 continue;
176 shpool = (ngx_slab_pool_t *) lzcf->shm_zone->shm.addr; 159 }
177 160
178 ngx_shmtx_lock(&shpool->mutex); 161 len = vv->len;
179 162
180 node = ngx_http_limit_zone_lookup(ctx->rbtree, vv, hash); 163 if (len == 0) {
181 164 continue;
182 if (node == NULL) { 165 }
183 166
184 n = offsetof(ngx_rbtree_node_t, color) 167 if (len > 255) {
185 + offsetof(ngx_http_limit_zone_node_t, data) 168 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
186 + len; 169 "the value of the \"%V\" variable "
187 170 "is more than 255 bytes: \"%v\"",
188 node = ngx_slab_alloc_locked(shpool, n); 171 &ctx->var, vv);
172 continue;
173 }
174
175 hash = ngx_crc32_short(vv->data, len);
176
177 shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr;
178
179 ngx_shmtx_lock(&shpool->mutex);
180
181 node = ngx_http_limit_zone_lookup(ctx->rbtree, vv, hash);
182
189 if (node == NULL) { 183 if (node == NULL) {
190 ngx_shmtx_unlock(&shpool->mutex); 184
191 return NGX_HTTP_SERVICE_UNAVAILABLE; 185 n = offsetof(ngx_rbtree_node_t, color)
192 } 186 + offsetof(ngx_http_limit_zone_node_t, data)
193 187 + len;
194 lz = (ngx_http_limit_zone_node_t *) &node->color; 188
195 189 node = ngx_slab_alloc_locked(shpool, n);
196 node->key = hash; 190
197 lz->len = (u_char) len; 191 if (node == NULL) {
198 lz->conn = 1; 192 ngx_shmtx_unlock(&shpool->mutex);
199 ngx_memcpy(lz->data, vv->data, len); 193 ngx_http_limit_zone_cleanup_all(r->pool);
200 194 return NGX_HTTP_SERVICE_UNAVAILABLE;
201 ngx_rbtree_insert(ctx->rbtree, node); 195 }
202 196
203 } else { 197 lz = (ngx_http_limit_zone_node_t *) &node->color;
204 198
205 lz = (ngx_http_limit_zone_node_t *) &node->color; 199 node->key = hash;
206 200 lz->len = (u_char) len;
207 if ((ngx_uint_t) lz->conn >= lzcf->conn) { 201 lz->conn = 1;
208 202 ngx_memcpy(lz->data, vv->data, len);
209 ngx_shmtx_unlock(&shpool->mutex); 203
210 204 ngx_rbtree_insert(ctx->rbtree, node);
211 ngx_log_error(lzcf->log_level, r->connection->log, 0, 205
212 "limiting connections by zone \"%V\"", 206 } else {
213 &lzcf->shm_zone->shm.name); 207
214 208 lz = (ngx_http_limit_zone_node_t *) &node->color;
215 return NGX_HTTP_SERVICE_UNAVAILABLE; 209
216 } 210 if ((ngx_uint_t) lz->conn >= limits[i].conn) {
217 211
218 lz->conn++; 212 ngx_shmtx_unlock(&shpool->mutex);
219 } 213
220 214 ngx_log_error(lzcf->log_level, r->connection->log, 0,
221 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 215 "limiting connections by zone \"%V\"",
222 "limit zone: %08XD %d", node->key, lz->conn); 216 &limits[i].shm_zone->shm.name);
223 217
224 ngx_shmtx_unlock(&shpool->mutex); 218 ngx_http_limit_zone_cleanup_all(r->pool);
225 219 return NGX_HTTP_SERVICE_UNAVAILABLE;
226 cln->handler = ngx_http_limit_zone_cleanup; 220 }
227 lzcln = cln->data; 221
228 222 lz->conn++;
229 lzcln->shm_zone = lzcf->shm_zone; 223 }
230 lzcln->node = node; 224
225 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
226 "limit zone: %08XD %d", node->key, lz->conn);
227
228 ngx_shmtx_unlock(&shpool->mutex);
229
230 cln = ngx_pool_cleanup_add(r->pool,
231 sizeof(ngx_http_limit_zone_cleanup_t));
232 if (cln == NULL) {
233 return NGX_HTTP_INTERNAL_SERVER_ERROR;
234 }
235
236 cln->handler = ngx_http_limit_zone_cleanup;
237 lzcln = cln->data;
238
239 lzcln->shm_zone = limits[i].shm_zone;
240 lzcln->node = node;
241 }
231 242
232 return NGX_DECLINED; 243 return NGX_DECLINED;
233 } 244 }
234 245
235 246
345 ngx_rbtree_delete(ctx->rbtree, node); 356 ngx_rbtree_delete(ctx->rbtree, node);
346 ngx_slab_free_locked(shpool, node); 357 ngx_slab_free_locked(shpool, node);
347 } 358 }
348 359
349 ngx_shmtx_unlock(&shpool->mutex); 360 ngx_shmtx_unlock(&shpool->mutex);
361 }
362
363
364 static ngx_inline void
365 ngx_http_limit_zone_cleanup_all(ngx_pool_t *pool)
366 {
367 ngx_pool_cleanup_t *cln;
368
369 cln = pool->cleanup;
370
371 while (cln && cln->handler == ngx_http_limit_zone_cleanup) {
372 ngx_http_limit_zone_cleanup(cln->data);
373 cln = cln->next;
374 }
375
376 pool->cleanup = cln;
350 } 377 }
351 378
352 379
353 static ngx_int_t 380 static ngx_int_t
354 ngx_http_limit_zone_init_zone(ngx_shm_zone_t *shm_zone, void *data) 381 ngx_http_limit_zone_init_zone(ngx_shm_zone_t *shm_zone, void *data)
424 } 451 }
425 452
426 /* 453 /*
427 * set by ngx_pcalloc(): 454 * set by ngx_pcalloc():
428 * 455 *
429 * conf->shm_zone = NULL; 456 * conf->limits.elts = NULL;
430 * conf->conn = 0;
431 */ 457 */
432 458
433 conf->log_level = NGX_CONF_UNSET_UINT; 459 conf->log_level = NGX_CONF_UNSET_UINT;
434 460
435 return conf; 461 return conf;
440 ngx_http_limit_zone_merge_conf(ngx_conf_t *cf, void *parent, void *child) 466 ngx_http_limit_zone_merge_conf(ngx_conf_t *cf, void *parent, void *child)
441 { 467 {
442 ngx_http_limit_zone_conf_t *prev = parent; 468 ngx_http_limit_zone_conf_t *prev = parent;
443 ngx_http_limit_zone_conf_t *conf = child; 469 ngx_http_limit_zone_conf_t *conf = child;
444 470
445 if (conf->shm_zone == NULL) { 471 if (conf->limits.elts == NULL) {
446 *conf = *prev; 472 *conf = *prev;
447 } 473 }
448 474
449 ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR); 475 ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);
450 476
521 547
522 548
523 static char * 549 static char *
524 ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 550 ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
525 { 551 {
526 ngx_http_limit_zone_conf_t *lzcf = conf; 552 ngx_shm_zone_t *shm_zone;
527 553 ngx_http_limit_zone_conf_t *lzcf = conf;
554 ngx_http_limit_zone_limit_t *limit, *limits;
555
556 ngx_str_t *value;
528 ngx_int_t n; 557 ngx_int_t n;
529 ngx_str_t *value; 558 ngx_uint_t i;
530
531 if (lzcf->shm_zone) {
532 return "is duplicate";
533 }
534 559
535 value = cf->args->elts; 560 value = cf->args->elts;
536 561
537 lzcf->shm_zone = ngx_shared_memory_add(cf, &value[1], 0, 562 shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
538 &ngx_http_limit_zone_module); 563 &ngx_http_limit_zone_module);
539 if (lzcf->shm_zone == NULL) { 564 if (shm_zone == NULL) {
540 return NGX_CONF_ERROR; 565 return NGX_CONF_ERROR;
566 }
567
568 limits = lzcf->limits.elts;
569
570 if (limits == NULL) {
571 if (ngx_array_init(&lzcf->limits, cf->pool, 1,
572 sizeof(ngx_http_limit_zone_limit_t))
573 != NGX_OK)
574 {
575 return NGX_CONF_ERROR;
576 }
577 }
578
579 for (i = 0; i < lzcf->limits.nelts; i++) {
580 if (shm_zone == limits[i].shm_zone) {
581 return "is duplicate";
582 }
541 } 583 }
542 584
543 n = ngx_atoi(value[2].data, value[2].len); 585 n = ngx_atoi(value[2].data, value[2].len);
544 if (n <= 0) { 586 if (n <= 0) {
545 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 587 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
551 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 593 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
552 "connection limit must be less 65536"); 594 "connection limit must be less 65536");
553 return NGX_CONF_ERROR; 595 return NGX_CONF_ERROR;
554 } 596 }
555 597
556 lzcf->conn = n; 598 limit = ngx_array_push(&lzcf->limits);
599 limit->conn = n;
600 limit->shm_zone = shm_zone;
557 601
558 return NGX_CONF_OK; 602 return NGX_CONF_OK;
559 } 603 }
560 604
561 605