Mercurial > hg > nginx
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 |