Mercurial > hg > nginx
comparison src/http/modules/ngx_http_limit_conn_module.c @ 4280:91874133fb27
Renamed ngx_http_limit_zone_module to ngx_http_limit_conn_module.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Mon, 14 Nov 2011 14:04:42 +0000 |
parents | src/http/modules/ngx_http_limit_zone_module.c@260d591cb6a3 |
children | 7697412a0921 |
comparison
equal
deleted
inserted
replaced
4279:21167183825d | 4280:91874133fb27 |
---|---|
1 | |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_http.h> | |
10 | |
11 | |
12 typedef struct { | |
13 u_char color; | |
14 u_char len; | |
15 u_short conn; | |
16 u_char data[1]; | |
17 } ngx_http_limit_conn_node_t; | |
18 | |
19 | |
20 typedef struct { | |
21 ngx_shm_zone_t *shm_zone; | |
22 ngx_rbtree_node_t *node; | |
23 } ngx_http_limit_conn_cleanup_t; | |
24 | |
25 | |
26 typedef struct { | |
27 ngx_rbtree_t *rbtree; | |
28 ngx_int_t index; | |
29 ngx_str_t var; | |
30 } ngx_http_limit_conn_ctx_t; | |
31 | |
32 | |
33 typedef struct { | |
34 ngx_shm_zone_t *shm_zone; | |
35 ngx_uint_t conn; | |
36 } ngx_http_limit_conn_limit_t; | |
37 | |
38 | |
39 typedef struct { | |
40 ngx_array_t limits; | |
41 ngx_uint_t log_level; | |
42 } ngx_http_limit_conn_conf_t; | |
43 | |
44 | |
45 static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, | |
46 ngx_http_variable_value_t *vv, uint32_t hash); | |
47 static void ngx_http_limit_conn_cleanup(void *data); | |
48 static ngx_inline void ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool); | |
49 | |
50 static void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf); | |
51 static char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, | |
52 void *child); | |
53 static char *ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, | |
54 void *conf); | |
55 static char *ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd, | |
56 void *conf); | |
57 static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, | |
58 void *conf); | |
59 static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf); | |
60 | |
61 | |
62 static ngx_conf_deprecated_t ngx_conf_deprecated_limit_zone = { | |
63 ngx_conf_deprecated, "limit_zone", "limit_conn_zone" | |
64 }; | |
65 | |
66 | |
67 static ngx_conf_enum_t ngx_http_limit_conn_log_levels[] = { | |
68 { ngx_string("info"), NGX_LOG_INFO }, | |
69 { ngx_string("notice"), NGX_LOG_NOTICE }, | |
70 { ngx_string("warn"), NGX_LOG_WARN }, | |
71 { ngx_string("error"), NGX_LOG_ERR }, | |
72 { ngx_null_string, 0 } | |
73 }; | |
74 | |
75 | |
76 static ngx_command_t ngx_http_limit_conn_commands[] = { | |
77 | |
78 { ngx_string("limit_conn_zone"), | |
79 NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2, | |
80 ngx_http_limit_conn_zone, | |
81 0, | |
82 0, | |
83 NULL }, | |
84 | |
85 { ngx_string("limit_zone"), | |
86 NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3, | |
87 ngx_http_limit_zone, | |
88 0, | |
89 0, | |
90 NULL }, | |
91 | |
92 { ngx_string("limit_conn"), | |
93 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, | |
94 ngx_http_limit_conn, | |
95 NGX_HTTP_LOC_CONF_OFFSET, | |
96 0, | |
97 NULL }, | |
98 | |
99 { ngx_string("limit_conn_log_level"), | |
100 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
101 ngx_conf_set_enum_slot, | |
102 NGX_HTTP_LOC_CONF_OFFSET, | |
103 offsetof(ngx_http_limit_conn_conf_t, log_level), | |
104 &ngx_http_limit_conn_log_levels }, | |
105 | |
106 ngx_null_command | |
107 }; | |
108 | |
109 | |
110 static ngx_http_module_t ngx_http_limit_conn_module_ctx = { | |
111 NULL, /* preconfiguration */ | |
112 ngx_http_limit_conn_init, /* postconfiguration */ | |
113 | |
114 NULL, /* create main configuration */ | |
115 NULL, /* init main configuration */ | |
116 | |
117 NULL, /* create server configuration */ | |
118 NULL, /* merge server configuration */ | |
119 | |
120 ngx_http_limit_conn_create_conf, /* create location configration */ | |
121 ngx_http_limit_conn_merge_conf /* merge location configration */ | |
122 }; | |
123 | |
124 | |
125 ngx_module_t ngx_http_limit_conn_module = { | |
126 NGX_MODULE_V1, | |
127 &ngx_http_limit_conn_module_ctx, /* module context */ | |
128 ngx_http_limit_conn_commands, /* module directives */ | |
129 NGX_HTTP_MODULE, /* module type */ | |
130 NULL, /* init master */ | |
131 NULL, /* init module */ | |
132 NULL, /* init process */ | |
133 NULL, /* init thread */ | |
134 NULL, /* exit thread */ | |
135 NULL, /* exit process */ | |
136 NULL, /* exit master */ | |
137 NGX_MODULE_V1_PADDING | |
138 }; | |
139 | |
140 | |
141 static ngx_int_t | |
142 ngx_http_limit_conn_handler(ngx_http_request_t *r) | |
143 { | |
144 size_t len, n; | |
145 uint32_t hash; | |
146 ngx_uint_t i; | |
147 ngx_slab_pool_t *shpool; | |
148 ngx_rbtree_node_t *node; | |
149 ngx_pool_cleanup_t *cln; | |
150 ngx_http_variable_value_t *vv; | |
151 ngx_http_limit_conn_ctx_t *ctx; | |
152 ngx_http_limit_conn_node_t *lc; | |
153 ngx_http_limit_conn_conf_t *lccf; | |
154 ngx_http_limit_conn_limit_t *limits; | |
155 ngx_http_limit_conn_cleanup_t *lccln; | |
156 | |
157 if (r->main->limit_conn_set) { | |
158 return NGX_DECLINED; | |
159 } | |
160 | |
161 r->main->limit_conn_set = 1; | |
162 | |
163 lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module); | |
164 limits = lccf->limits.elts; | |
165 | |
166 for (i = 0; i < lccf->limits.nelts; i++) { | |
167 ctx = limits[i].shm_zone->data; | |
168 | |
169 vv = ngx_http_get_indexed_variable(r, ctx->index); | |
170 | |
171 if (vv == NULL || vv->not_found) { | |
172 continue; | |
173 } | |
174 | |
175 len = vv->len; | |
176 | |
177 if (len == 0) { | |
178 continue; | |
179 } | |
180 | |
181 if (len > 255) { | |
182 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
183 "the value of the \"%V\" variable " | |
184 "is more than 255 bytes: \"%v\"", | |
185 &ctx->var, vv); | |
186 continue; | |
187 } | |
188 | |
189 hash = ngx_crc32_short(vv->data, len); | |
190 | |
191 shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr; | |
192 | |
193 ngx_shmtx_lock(&shpool->mutex); | |
194 | |
195 node = ngx_http_limit_conn_lookup(ctx->rbtree, vv, hash); | |
196 | |
197 if (node == NULL) { | |
198 | |
199 n = offsetof(ngx_rbtree_node_t, color) | |
200 + offsetof(ngx_http_limit_conn_node_t, data) | |
201 + len; | |
202 | |
203 node = ngx_slab_alloc_locked(shpool, n); | |
204 | |
205 if (node == NULL) { | |
206 ngx_shmtx_unlock(&shpool->mutex); | |
207 ngx_http_limit_conn_cleanup_all(r->pool); | |
208 return NGX_HTTP_SERVICE_UNAVAILABLE; | |
209 } | |
210 | |
211 lc = (ngx_http_limit_conn_node_t *) &node->color; | |
212 | |
213 node->key = hash; | |
214 lc->len = (u_char) len; | |
215 lc->conn = 1; | |
216 ngx_memcpy(lc->data, vv->data, len); | |
217 | |
218 ngx_rbtree_insert(ctx->rbtree, node); | |
219 | |
220 } else { | |
221 | |
222 lc = (ngx_http_limit_conn_node_t *) &node->color; | |
223 | |
224 if ((ngx_uint_t) lc->conn >= limits[i].conn) { | |
225 | |
226 ngx_shmtx_unlock(&shpool->mutex); | |
227 | |
228 ngx_log_error(lccf->log_level, r->connection->log, 0, | |
229 "limiting connections by zone \"%V\"", | |
230 &limits[i].shm_zone->shm.name); | |
231 | |
232 ngx_http_limit_conn_cleanup_all(r->pool); | |
233 return NGX_HTTP_SERVICE_UNAVAILABLE; | |
234 } | |
235 | |
236 lc->conn++; | |
237 } | |
238 | |
239 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
240 "limit zone: %08XD %d", node->key, lc->conn); | |
241 | |
242 ngx_shmtx_unlock(&shpool->mutex); | |
243 | |
244 cln = ngx_pool_cleanup_add(r->pool, | |
245 sizeof(ngx_http_limit_conn_cleanup_t)); | |
246 if (cln == NULL) { | |
247 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
248 } | |
249 | |
250 cln->handler = ngx_http_limit_conn_cleanup; | |
251 lccln = cln->data; | |
252 | |
253 lccln->shm_zone = limits[i].shm_zone; | |
254 lccln->node = node; | |
255 } | |
256 | |
257 return NGX_DECLINED; | |
258 } | |
259 | |
260 | |
261 static void | |
262 ngx_http_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp, | |
263 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) | |
264 { | |
265 ngx_rbtree_node_t **p; | |
266 ngx_http_limit_conn_node_t *lcn, *lcnt; | |
267 | |
268 for ( ;; ) { | |
269 | |
270 if (node->key < temp->key) { | |
271 | |
272 p = &temp->left; | |
273 | |
274 } else if (node->key > temp->key) { | |
275 | |
276 p = &temp->right; | |
277 | |
278 } else { /* node->key == temp->key */ | |
279 | |
280 lcn = (ngx_http_limit_conn_node_t *) &node->color; | |
281 lcnt = (ngx_http_limit_conn_node_t *) &temp->color; | |
282 | |
283 p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0) | |
284 ? &temp->left : &temp->right; | |
285 } | |
286 | |
287 if (*p == sentinel) { | |
288 break; | |
289 } | |
290 | |
291 temp = *p; | |
292 } | |
293 | |
294 *p = node; | |
295 node->parent = temp; | |
296 node->left = sentinel; | |
297 node->right = sentinel; | |
298 ngx_rbt_red(node); | |
299 } | |
300 | |
301 | |
302 static ngx_rbtree_node_t * | |
303 ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_http_variable_value_t *vv, | |
304 uint32_t hash) | |
305 { | |
306 ngx_int_t rc; | |
307 ngx_rbtree_node_t *node, *sentinel; | |
308 ngx_http_limit_conn_node_t *lcn; | |
309 | |
310 node = rbtree->root; | |
311 sentinel = rbtree->sentinel; | |
312 | |
313 while (node != sentinel) { | |
314 | |
315 if (hash < node->key) { | |
316 node = node->left; | |
317 continue; | |
318 } | |
319 | |
320 if (hash > node->key) { | |
321 node = node->right; | |
322 continue; | |
323 } | |
324 | |
325 /* hash == node->key */ | |
326 | |
327 do { | |
328 lcn = (ngx_http_limit_conn_node_t *) &node->color; | |
329 | |
330 rc = ngx_memn2cmp(vv->data, lcn->data, | |
331 (size_t) vv->len, (size_t) lcn->len); | |
332 if (rc == 0) { | |
333 return node; | |
334 } | |
335 | |
336 node = (rc < 0) ? node->left : node->right; | |
337 | |
338 } while (node != sentinel && hash == node->key); | |
339 | |
340 break; | |
341 } | |
342 | |
343 return NULL; | |
344 } | |
345 | |
346 | |
347 static void | |
348 ngx_http_limit_conn_cleanup(void *data) | |
349 { | |
350 ngx_http_limit_conn_cleanup_t *lccln = data; | |
351 | |
352 ngx_slab_pool_t *shpool; | |
353 ngx_rbtree_node_t *node; | |
354 ngx_http_limit_conn_ctx_t *ctx; | |
355 ngx_http_limit_conn_node_t *lc; | |
356 | |
357 ctx = lccln->shm_zone->data; | |
358 shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr; | |
359 node = lccln->node; | |
360 lc = (ngx_http_limit_conn_node_t *) &node->color; | |
361 | |
362 ngx_shmtx_lock(&shpool->mutex); | |
363 | |
364 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0, | |
365 "limit zone cleanup: %08XD %d", node->key, lc->conn); | |
366 | |
367 lc->conn--; | |
368 | |
369 if (lc->conn == 0) { | |
370 ngx_rbtree_delete(ctx->rbtree, node); | |
371 ngx_slab_free_locked(shpool, node); | |
372 } | |
373 | |
374 ngx_shmtx_unlock(&shpool->mutex); | |
375 } | |
376 | |
377 | |
378 static ngx_inline void | |
379 ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool) | |
380 { | |
381 ngx_pool_cleanup_t *cln; | |
382 | |
383 cln = pool->cleanup; | |
384 | |
385 while (cln && cln->handler == ngx_http_limit_conn_cleanup) { | |
386 ngx_http_limit_conn_cleanup(cln->data); | |
387 cln = cln->next; | |
388 } | |
389 | |
390 pool->cleanup = cln; | |
391 } | |
392 | |
393 | |
394 static ngx_int_t | |
395 ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data) | |
396 { | |
397 ngx_http_limit_conn_ctx_t *octx = data; | |
398 | |
399 size_t len; | |
400 ngx_slab_pool_t *shpool; | |
401 ngx_rbtree_node_t *sentinel; | |
402 ngx_http_limit_conn_ctx_t *ctx; | |
403 | |
404 ctx = shm_zone->data; | |
405 | |
406 if (octx) { | |
407 if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) { | |
408 ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, | |
409 "limit_conn_zone \"%V\" uses the \"%V\" variable " | |
410 "while previously it used the \"%V\" variable", | |
411 &shm_zone->shm.name, &ctx->var, &octx->var); | |
412 return NGX_ERROR; | |
413 } | |
414 | |
415 ctx->rbtree = octx->rbtree; | |
416 | |
417 return NGX_OK; | |
418 } | |
419 | |
420 shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; | |
421 | |
422 if (shm_zone->shm.exists) { | |
423 ctx->rbtree = shpool->data; | |
424 | |
425 return NGX_OK; | |
426 } | |
427 | |
428 ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); | |
429 if (ctx->rbtree == NULL) { | |
430 return NGX_ERROR; | |
431 } | |
432 | |
433 shpool->data = ctx->rbtree; | |
434 | |
435 sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); | |
436 if (sentinel == NULL) { | |
437 return NGX_ERROR; | |
438 } | |
439 | |
440 ngx_rbtree_init(ctx->rbtree, sentinel, | |
441 ngx_http_limit_conn_rbtree_insert_value); | |
442 | |
443 len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len; | |
444 | |
445 shpool->log_ctx = ngx_slab_alloc(shpool, len); | |
446 if (shpool->log_ctx == NULL) { | |
447 return NGX_ERROR; | |
448 } | |
449 | |
450 ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", | |
451 &shm_zone->shm.name); | |
452 | |
453 return NGX_OK; | |
454 } | |
455 | |
456 | |
457 static void * | |
458 ngx_http_limit_conn_create_conf(ngx_conf_t *cf) | |
459 { | |
460 ngx_http_limit_conn_conf_t *conf; | |
461 | |
462 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_conf_t)); | |
463 if (conf == NULL) { | |
464 return NULL; | |
465 } | |
466 | |
467 /* | |
468 * set by ngx_pcalloc(): | |
469 * | |
470 * conf->limits.elts = NULL; | |
471 */ | |
472 | |
473 conf->log_level = NGX_CONF_UNSET_UINT; | |
474 | |
475 return conf; | |
476 } | |
477 | |
478 | |
479 static char * | |
480 ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child) | |
481 { | |
482 ngx_http_limit_conn_conf_t *prev = parent; | |
483 ngx_http_limit_conn_conf_t *conf = child; | |
484 | |
485 if (conf->limits.elts == NULL) { | |
486 *conf = *prev; | |
487 } | |
488 | |
489 ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR); | |
490 | |
491 return NGX_CONF_OK; | |
492 } | |
493 | |
494 | |
495 static char * | |
496 ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
497 { | |
498 u_char *p; | |
499 ssize_t size; | |
500 ngx_str_t *value, name, s; | |
501 ngx_uint_t i; | |
502 ngx_shm_zone_t *shm_zone; | |
503 ngx_http_limit_conn_ctx_t *ctx; | |
504 | |
505 value = cf->args->elts; | |
506 | |
507 ctx = NULL; | |
508 size = 0; | |
509 name.len = 0; | |
510 | |
511 for (i = 1; i < cf->args->nelts; i++) { | |
512 | |
513 if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { | |
514 | |
515 name.data = value[i].data + 5; | |
516 | |
517 p = (u_char *) ngx_strchr(name.data, ':'); | |
518 | |
519 if (p == NULL) { | |
520 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
521 "invalid zone size \"%V\"", &value[i]); | |
522 return NGX_CONF_ERROR; | |
523 } | |
524 | |
525 name.len = p - name.data; | |
526 | |
527 s.data = p + 1; | |
528 s.len = value[i].data + value[i].len - s.data; | |
529 | |
530 size = ngx_parse_size(&s); | |
531 | |
532 if (size == NGX_ERROR) { | |
533 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
534 "invalid zone size \"%V\"", &value[i]); | |
535 return NGX_CONF_ERROR; | |
536 } | |
537 | |
538 if (size < (ssize_t) (8 * ngx_pagesize)) { | |
539 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
540 "zone \"%V\" is too small", &value[i]); | |
541 return NGX_CONF_ERROR; | |
542 } | |
543 | |
544 continue; | |
545 } | |
546 | |
547 if (value[i].data[0] == '$') { | |
548 | |
549 value[i].len--; | |
550 value[i].data++; | |
551 | |
552 ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t)); | |
553 if (ctx == NULL) { | |
554 return NGX_CONF_ERROR; | |
555 } | |
556 | |
557 ctx->index = ngx_http_get_variable_index(cf, &value[i]); | |
558 if (ctx->index == NGX_ERROR) { | |
559 return NGX_CONF_ERROR; | |
560 } | |
561 | |
562 ctx->var = value[i]; | |
563 | |
564 continue; | |
565 } | |
566 | |
567 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
568 "invalid parameter \"%V\"", &value[i]); | |
569 return NGX_CONF_ERROR; | |
570 } | |
571 | |
572 if (name.len == 0) { | |
573 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
574 "\"%V\" must have \"zone\" parameter", | |
575 &cmd->name); | |
576 return NGX_CONF_ERROR; | |
577 } | |
578 | |
579 if (ctx == NULL) { | |
580 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
581 "no variable is defined for %V \"%V\"", | |
582 &cmd->name, &name); | |
583 return NGX_CONF_ERROR; | |
584 } | |
585 | |
586 shm_zone = ngx_shared_memory_add(cf, &name, size, | |
587 &ngx_http_limit_conn_module); | |
588 if (shm_zone == NULL) { | |
589 return NGX_CONF_ERROR; | |
590 } | |
591 | |
592 if (shm_zone->data) { | |
593 ctx = shm_zone->data; | |
594 | |
595 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
596 "%V \"%V\" is already bound to variable \"%V\"", | |
597 &cmd->name, &name, &ctx->var); | |
598 return NGX_CONF_ERROR; | |
599 } | |
600 | |
601 shm_zone->init = ngx_http_limit_conn_init_zone; | |
602 shm_zone->data = ctx; | |
603 | |
604 return NGX_CONF_OK; | |
605 } | |
606 | |
607 | |
608 static char * | |
609 ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
610 { | |
611 ssize_t n; | |
612 ngx_str_t *value; | |
613 ngx_shm_zone_t *shm_zone; | |
614 ngx_http_limit_conn_ctx_t *ctx; | |
615 | |
616 ngx_conf_deprecated(cf, &ngx_conf_deprecated_limit_zone, NULL); | |
617 | |
618 value = cf->args->elts; | |
619 | |
620 if (value[2].data[0] != '$') { | |
621 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
622 "invalid variable name \"%V\"", &value[2]); | |
623 return NGX_CONF_ERROR; | |
624 } | |
625 | |
626 value[2].len--; | |
627 value[2].data++; | |
628 | |
629 ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t)); | |
630 if (ctx == NULL) { | |
631 return NGX_CONF_ERROR; | |
632 } | |
633 | |
634 ctx->index = ngx_http_get_variable_index(cf, &value[2]); | |
635 if (ctx->index == NGX_ERROR) { | |
636 return NGX_CONF_ERROR; | |
637 } | |
638 | |
639 ctx->var = value[2]; | |
640 | |
641 n = ngx_parse_size(&value[3]); | |
642 | |
643 if (n == NGX_ERROR) { | |
644 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
645 "invalid size of limit_zone \"%V\"", &value[3]); | |
646 return NGX_CONF_ERROR; | |
647 } | |
648 | |
649 if (n < (ngx_int_t) (8 * ngx_pagesize)) { | |
650 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
651 "limit_zone \"%V\" is too small", &value[1]); | |
652 return NGX_CONF_ERROR; | |
653 } | |
654 | |
655 | |
656 shm_zone = ngx_shared_memory_add(cf, &value[1], n, | |
657 &ngx_http_limit_conn_module); | |
658 if (shm_zone == NULL) { | |
659 return NGX_CONF_ERROR; | |
660 } | |
661 | |
662 if (shm_zone->data) { | |
663 ctx = shm_zone->data; | |
664 | |
665 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
666 "limit_zone \"%V\" is already bound to variable \"%V\"", | |
667 &value[1], &ctx->var); | |
668 return NGX_CONF_ERROR; | |
669 } | |
670 | |
671 shm_zone->init = ngx_http_limit_conn_init_zone; | |
672 shm_zone->data = ctx; | |
673 | |
674 return NGX_CONF_OK; | |
675 } | |
676 | |
677 | |
678 static char * | |
679 ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
680 { | |
681 ngx_shm_zone_t *shm_zone; | |
682 ngx_http_limit_conn_conf_t *lccf = conf; | |
683 ngx_http_limit_conn_limit_t *limit, *limits; | |
684 | |
685 ngx_str_t *value; | |
686 ngx_int_t n; | |
687 ngx_uint_t i; | |
688 | |
689 value = cf->args->elts; | |
690 | |
691 shm_zone = ngx_shared_memory_add(cf, &value[1], 0, | |
692 &ngx_http_limit_conn_module); | |
693 if (shm_zone == NULL) { | |
694 return NGX_CONF_ERROR; | |
695 } | |
696 | |
697 limits = lccf->limits.elts; | |
698 | |
699 if (limits == NULL) { | |
700 if (ngx_array_init(&lccf->limits, cf->pool, 1, | |
701 sizeof(ngx_http_limit_conn_limit_t)) | |
702 != NGX_OK) | |
703 { | |
704 return NGX_CONF_ERROR; | |
705 } | |
706 } | |
707 | |
708 for (i = 0; i < lccf->limits.nelts; i++) { | |
709 if (shm_zone == limits[i].shm_zone) { | |
710 return "is duplicate"; | |
711 } | |
712 } | |
713 | |
714 n = ngx_atoi(value[2].data, value[2].len); | |
715 if (n <= 0) { | |
716 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
717 "invalid number of connections \"%V\"", &value[2]); | |
718 return NGX_CONF_ERROR; | |
719 } | |
720 | |
721 if (n > 65535) { | |
722 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
723 "connection limit must be less 65536"); | |
724 return NGX_CONF_ERROR; | |
725 } | |
726 | |
727 limit = ngx_array_push(&lccf->limits); | |
728 limit->conn = n; | |
729 limit->shm_zone = shm_zone; | |
730 | |
731 return NGX_CONF_OK; | |
732 } | |
733 | |
734 | |
735 static ngx_int_t | |
736 ngx_http_limit_conn_init(ngx_conf_t *cf) | |
737 { | |
738 ngx_http_handler_pt *h; | |
739 ngx_http_core_main_conf_t *cmcf; | |
740 | |
741 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); | |
742 | |
743 h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); | |
744 if (h == NULL) { | |
745 return NGX_ERROR; | |
746 } | |
747 | |
748 *h = ngx_http_limit_conn_handler; | |
749 | |
750 return NGX_OK; | |
751 } |