Update and clean Tomato RAF files
[tomato.git] / release / src / router / nginx / src / http / modules / ngx_http_limit_req_module.c
blob90434c956a6f418b3898efdc4118474557029df0
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
13 typedef struct {
14 u_char color;
15 u_char dummy;
16 u_short len;
17 ngx_queue_t queue;
18 ngx_msec_t last;
19 /* integer value, 1 corresponds to 0.001 r/s */
20 ngx_uint_t excess;
21 ngx_uint_t count;
22 u_char data[1];
23 } ngx_http_limit_req_node_t;
26 typedef struct {
27 ngx_rbtree_t rbtree;
28 ngx_rbtree_node_t sentinel;
29 ngx_queue_t queue;
30 } ngx_http_limit_req_shctx_t;
33 typedef struct {
34 ngx_http_limit_req_shctx_t *sh;
35 ngx_slab_pool_t *shpool;
36 /* integer value, 1 corresponds to 0.001 r/s */
37 ngx_uint_t rate;
38 ngx_int_t index;
39 ngx_str_t var;
40 ngx_http_limit_req_node_t *node;
41 } ngx_http_limit_req_ctx_t;
44 typedef struct {
45 ngx_shm_zone_t *shm_zone;
46 /* integer value, 1 corresponds to 0.001 r/s */
47 ngx_uint_t burst;
48 ngx_uint_t nodelay; /* unsigned nodelay:1 */
49 } ngx_http_limit_req_limit_t;
52 typedef struct {
53 ngx_array_t limits;
54 ngx_uint_t limit_log_level;
55 ngx_uint_t delay_log_level;
56 ngx_uint_t status_code;
57 } ngx_http_limit_req_conf_t;
60 static void ngx_http_limit_req_delay(ngx_http_request_t *r);
61 static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit,
62 ngx_uint_t hash, u_char *data, size_t len, ngx_uint_t *ep,
63 ngx_uint_t account);
64 static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits,
65 ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit);
66 static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
67 ngx_uint_t n);
69 static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf);
70 static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent,
71 void *child);
72 static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd,
73 void *conf);
74 static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd,
75 void *conf);
76 static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf);
79 static ngx_conf_enum_t ngx_http_limit_req_log_levels[] = {
80 { ngx_string("info"), NGX_LOG_INFO },
81 { ngx_string("notice"), NGX_LOG_NOTICE },
82 { ngx_string("warn"), NGX_LOG_WARN },
83 { ngx_string("error"), NGX_LOG_ERR },
84 { ngx_null_string, 0 }
88 static ngx_conf_num_bounds_t ngx_http_limit_req_status_bounds = {
89 ngx_conf_check_num_bounds, 400, 599
93 static ngx_command_t ngx_http_limit_req_commands[] = {
95 { ngx_string("limit_req_zone"),
96 NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
97 ngx_http_limit_req_zone,
100 NULL },
102 { ngx_string("limit_req"),
103 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
104 ngx_http_limit_req,
105 NGX_HTTP_LOC_CONF_OFFSET,
107 NULL },
109 { ngx_string("limit_req_log_level"),
110 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
111 ngx_conf_set_enum_slot,
112 NGX_HTTP_LOC_CONF_OFFSET,
113 offsetof(ngx_http_limit_req_conf_t, limit_log_level),
114 &ngx_http_limit_req_log_levels },
116 { ngx_string("limit_req_status"),
117 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
118 ngx_conf_set_num_slot,
119 NGX_HTTP_LOC_CONF_OFFSET,
120 offsetof(ngx_http_limit_req_conf_t, status_code),
121 &ngx_http_limit_req_status_bounds },
123 ngx_null_command
127 static ngx_http_module_t ngx_http_limit_req_module_ctx = {
128 NULL, /* preconfiguration */
129 ngx_http_limit_req_init, /* postconfiguration */
131 NULL, /* create main configuration */
132 NULL, /* init main configuration */
134 NULL, /* create server configuration */
135 NULL, /* merge server configuration */
137 ngx_http_limit_req_create_conf, /* create location configuration */
138 ngx_http_limit_req_merge_conf /* merge location configuration */
142 ngx_module_t ngx_http_limit_req_module = {
143 NGX_MODULE_V1,
144 &ngx_http_limit_req_module_ctx, /* module context */
145 ngx_http_limit_req_commands, /* module directives */
146 NGX_HTTP_MODULE, /* module type */
147 NULL, /* init master */
148 NULL, /* init module */
149 NULL, /* init process */
150 NULL, /* init thread */
151 NULL, /* exit thread */
152 NULL, /* exit process */
153 NULL, /* exit master */
154 NGX_MODULE_V1_PADDING
158 static ngx_int_t
159 ngx_http_limit_req_handler(ngx_http_request_t *r)
161 size_t len;
162 uint32_t hash;
163 ngx_int_t rc;
164 ngx_uint_t n, excess;
165 ngx_msec_t delay;
166 ngx_http_variable_value_t *vv;
167 ngx_http_limit_req_ctx_t *ctx;
168 ngx_http_limit_req_conf_t *lrcf;
169 ngx_http_limit_req_limit_t *limit, *limits;
171 if (r->main->limit_req_set) {
172 return NGX_DECLINED;
175 lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
176 limits = lrcf->limits.elts;
178 excess = 0;
180 rc = NGX_DECLINED;
182 #if (NGX_SUPPRESS_WARN)
183 limit = NULL;
184 #endif
186 for (n = 0; n < lrcf->limits.nelts; n++) {
188 limit = &limits[n];
190 ctx = limit->shm_zone->data;
192 vv = ngx_http_get_indexed_variable(r, ctx->index);
194 if (vv == NULL || vv->not_found) {
195 continue;
198 len = vv->len;
200 if (len == 0) {
201 continue;
204 if (len > 65535) {
205 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
206 "the value of the \"%V\" variable "
207 "is more than 65535 bytes: \"%v\"",
208 &ctx->var, vv);
209 continue;
212 hash = ngx_crc32_short(vv->data, len);
214 ngx_shmtx_lock(&ctx->shpool->mutex);
216 rc = ngx_http_limit_req_lookup(limit, hash, vv->data, len, &excess,
217 (n == lrcf->limits.nelts - 1));
219 ngx_shmtx_unlock(&ctx->shpool->mutex);
221 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
222 "limit_req[%ui]: %i %ui.%03ui",
223 n, rc, excess / 1000, excess % 1000);
225 if (rc != NGX_AGAIN) {
226 break;
230 if (rc == NGX_DECLINED) {
231 return NGX_DECLINED;
234 r->main->limit_req_set = 1;
236 if (rc == NGX_BUSY || rc == NGX_ERROR) {
238 if (rc == NGX_BUSY) {
239 ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
240 "limiting requests, excess: %ui.%03ui by zone \"%V\"",
241 excess / 1000, excess % 1000,
242 &limit->shm_zone->shm.name);
245 while (n--) {
246 ctx = limits[n].shm_zone->data;
248 if (ctx->node == NULL) {
249 continue;
252 ngx_shmtx_lock(&ctx->shpool->mutex);
254 ctx->node->count--;
256 ngx_shmtx_unlock(&ctx->shpool->mutex);
258 ctx->node = NULL;
261 return lrcf->status_code;
264 /* rc == NGX_AGAIN || rc == NGX_OK */
266 if (rc == NGX_AGAIN) {
267 excess = 0;
270 delay = ngx_http_limit_req_account(limits, n, &excess, &limit);
272 if (!delay) {
273 return NGX_DECLINED;
276 ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
277 "delaying request, excess: %ui.%03ui, by zone \"%V\"",
278 excess / 1000, excess % 1000, &limit->shm_zone->shm.name);
280 if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
281 return NGX_HTTP_INTERNAL_SERVER_ERROR;
284 r->read_event_handler = ngx_http_test_reading;
285 r->write_event_handler = ngx_http_limit_req_delay;
286 ngx_add_timer(r->connection->write, delay);
288 return NGX_AGAIN;
292 static void
293 ngx_http_limit_req_delay(ngx_http_request_t *r)
295 ngx_event_t *wev;
297 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
298 "limit_req delay");
300 wev = r->connection->write;
302 if (!wev->timedout) {
304 if (ngx_handle_write_event(wev, 0) != NGX_OK) {
305 ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
308 return;
311 wev->timedout = 0;
313 if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
314 ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
315 return;
318 r->read_event_handler = ngx_http_block_reading;
319 r->write_event_handler = ngx_http_core_run_phases;
321 ngx_http_core_run_phases(r);
325 static void
326 ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp,
327 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
329 ngx_rbtree_node_t **p;
330 ngx_http_limit_req_node_t *lrn, *lrnt;
332 for ( ;; ) {
334 if (node->key < temp->key) {
336 p = &temp->left;
338 } else if (node->key > temp->key) {
340 p = &temp->right;
342 } else { /* node->key == temp->key */
344 lrn = (ngx_http_limit_req_node_t *) &node->color;
345 lrnt = (ngx_http_limit_req_node_t *) &temp->color;
347 p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)
348 ? &temp->left : &temp->right;
351 if (*p == sentinel) {
352 break;
355 temp = *p;
358 *p = node;
359 node->parent = temp;
360 node->left = sentinel;
361 node->right = sentinel;
362 ngx_rbt_red(node);
366 static ngx_int_t
367 ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash,
368 u_char *data, size_t len, ngx_uint_t *ep, ngx_uint_t account)
370 size_t size;
371 ngx_int_t rc, excess;
372 ngx_time_t *tp;
373 ngx_msec_t now;
374 ngx_msec_int_t ms;
375 ngx_rbtree_node_t *node, *sentinel;
376 ngx_http_limit_req_ctx_t *ctx;
377 ngx_http_limit_req_node_t *lr;
379 tp = ngx_timeofday();
380 now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
382 ctx = limit->shm_zone->data;
384 node = ctx->sh->rbtree.root;
385 sentinel = ctx->sh->rbtree.sentinel;
387 while (node != sentinel) {
389 if (hash < node->key) {
390 node = node->left;
391 continue;
394 if (hash > node->key) {
395 node = node->right;
396 continue;
399 /* hash == node->key */
401 lr = (ngx_http_limit_req_node_t *) &node->color;
403 rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len);
405 if (rc == 0) {
406 ngx_queue_remove(&lr->queue);
407 ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
409 ms = (ngx_msec_int_t) (now - lr->last);
411 excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
413 if (excess < 0) {
414 excess = 0;
417 *ep = excess;
419 if ((ngx_uint_t) excess > limit->burst) {
420 return NGX_BUSY;
423 if (account) {
424 lr->excess = excess;
425 lr->last = now;
426 return NGX_OK;
429 lr->count++;
431 ctx->node = lr;
433 return NGX_AGAIN;
436 node = (rc < 0) ? node->left : node->right;
439 *ep = 0;
441 size = offsetof(ngx_rbtree_node_t, color)
442 + offsetof(ngx_http_limit_req_node_t, data)
443 + len;
445 ngx_http_limit_req_expire(ctx, 1);
447 node = ngx_slab_alloc_locked(ctx->shpool, size);
449 if (node == NULL) {
450 ngx_http_limit_req_expire(ctx, 0);
452 node = ngx_slab_alloc_locked(ctx->shpool, size);
453 if (node == NULL) {
454 return NGX_ERROR;
458 node->key = hash;
460 lr = (ngx_http_limit_req_node_t *) &node->color;
462 lr->len = (u_char) len;
463 lr->excess = 0;
465 ngx_memcpy(lr->data, data, len);
467 ngx_rbtree_insert(&ctx->sh->rbtree, node);
469 ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
471 if (account) {
472 lr->last = now;
473 lr->count = 0;
474 return NGX_OK;
477 lr->last = 0;
478 lr->count = 1;
480 ctx->node = lr;
482 return NGX_AGAIN;
486 static ngx_msec_t
487 ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n,
488 ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit)
490 ngx_int_t excess;
491 ngx_time_t *tp;
492 ngx_msec_t now, delay, max_delay;
493 ngx_msec_int_t ms;
494 ngx_http_limit_req_ctx_t *ctx;
495 ngx_http_limit_req_node_t *lr;
497 excess = *ep;
499 if (excess == 0 || (*limit)->nodelay) {
500 max_delay = 0;
502 } else {
503 ctx = (*limit)->shm_zone->data;
504 max_delay = excess * 1000 / ctx->rate;
507 while (n--) {
508 ctx = limits[n].shm_zone->data;
509 lr = ctx->node;
511 if (lr == NULL) {
512 continue;
515 ngx_shmtx_lock(&ctx->shpool->mutex);
517 tp = ngx_timeofday();
519 now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
520 ms = (ngx_msec_int_t) (now - lr->last);
522 excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
524 if (excess < 0) {
525 excess = 0;
528 lr->last = now;
529 lr->excess = excess;
530 lr->count--;
532 ngx_shmtx_unlock(&ctx->shpool->mutex);
534 ctx->node = NULL;
536 if (limits[n].nodelay) {
537 continue;
540 delay = excess * 1000 / ctx->rate;
542 if (delay > max_delay) {
543 max_delay = delay;
544 *ep = excess;
545 *limit = &limits[n];
549 return max_delay;
553 static void
554 ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
556 ngx_int_t excess;
557 ngx_time_t *tp;
558 ngx_msec_t now;
559 ngx_queue_t *q;
560 ngx_msec_int_t ms;
561 ngx_rbtree_node_t *node;
562 ngx_http_limit_req_node_t *lr;
564 tp = ngx_timeofday();
566 now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
569 * n == 1 deletes one or two zero rate entries
570 * n == 0 deletes oldest entry by force
571 * and one or two zero rate entries
574 while (n < 3) {
576 if (ngx_queue_empty(&ctx->sh->queue)) {
577 return;
580 q = ngx_queue_last(&ctx->sh->queue);
582 lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
584 if (lr->count) {
587 * There is not much sense in looking further,
588 * because we bump nodes on the lookup stage.
591 return;
594 if (n++ != 0) {
596 ms = (ngx_msec_int_t) (now - lr->last);
597 ms = ngx_abs(ms);
599 if (ms < 60000) {
600 return;
603 excess = lr->excess - ctx->rate * ms / 1000;
605 if (excess > 0) {
606 return;
610 ngx_queue_remove(q);
612 node = (ngx_rbtree_node_t *)
613 ((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
615 ngx_rbtree_delete(&ctx->sh->rbtree, node);
617 ngx_slab_free_locked(ctx->shpool, node);
622 static ngx_int_t
623 ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)
625 ngx_http_limit_req_ctx_t *octx = data;
627 size_t len;
628 ngx_http_limit_req_ctx_t *ctx;
630 ctx = shm_zone->data;
632 if (octx) {
633 if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) {
634 ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
635 "limit_req \"%V\" uses the \"%V\" variable "
636 "while previously it used the \"%V\" variable",
637 &shm_zone->shm.name, &ctx->var, &octx->var);
638 return NGX_ERROR;
641 ctx->sh = octx->sh;
642 ctx->shpool = octx->shpool;
644 return NGX_OK;
647 ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
649 if (shm_zone->shm.exists) {
650 ctx->sh = ctx->shpool->data;
652 return NGX_OK;
655 ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t));
656 if (ctx->sh == NULL) {
657 return NGX_ERROR;
660 ctx->shpool->data = ctx->sh;
662 ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
663 ngx_http_limit_req_rbtree_insert_value);
665 ngx_queue_init(&ctx->sh->queue);
667 len = sizeof(" in limit_req zone \"\"") + shm_zone->shm.name.len;
669 ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
670 if (ctx->shpool->log_ctx == NULL) {
671 return NGX_ERROR;
674 ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",
675 &shm_zone->shm.name);
677 return NGX_OK;
681 static void *
682 ngx_http_limit_req_create_conf(ngx_conf_t *cf)
684 ngx_http_limit_req_conf_t *conf;
686 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));
687 if (conf == NULL) {
688 return NULL;
692 * set by ngx_pcalloc():
694 * conf->limits.elts = NULL;
697 conf->limit_log_level = NGX_CONF_UNSET_UINT;
698 conf->status_code = NGX_CONF_UNSET_UINT;
700 return conf;
704 static char *
705 ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
707 ngx_http_limit_req_conf_t *prev = parent;
708 ngx_http_limit_req_conf_t *conf = child;
710 if (conf->limits.elts == NULL) {
711 conf->limits = prev->limits;
714 ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level,
715 NGX_LOG_ERR);
717 conf->delay_log_level = (conf->limit_log_level == NGX_LOG_INFO) ?
718 NGX_LOG_INFO : conf->limit_log_level + 1;
720 ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
721 NGX_HTTP_SERVICE_UNAVAILABLE);
723 return NGX_CONF_OK;
727 static char *
728 ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
730 u_char *p;
731 size_t len;
732 ssize_t size;
733 ngx_str_t *value, name, s;
734 ngx_int_t rate, scale;
735 ngx_uint_t i;
736 ngx_shm_zone_t *shm_zone;
737 ngx_http_limit_req_ctx_t *ctx;
739 value = cf->args->elts;
741 ctx = NULL;
742 size = 0;
743 rate = 1;
744 scale = 1;
745 name.len = 0;
747 for (i = 1; i < cf->args->nelts; i++) {
749 if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
751 name.data = value[i].data + 5;
753 p = (u_char *) ngx_strchr(name.data, ':');
755 if (p == NULL) {
756 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
757 "invalid zone size \"%V\"", &value[i]);
758 return NGX_CONF_ERROR;
761 name.len = p - name.data;
763 s.data = p + 1;
764 s.len = value[i].data + value[i].len - s.data;
766 size = ngx_parse_size(&s);
768 if (size == NGX_ERROR) {
769 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
770 "invalid zone size \"%V\"", &value[i]);
771 return NGX_CONF_ERROR;
774 if (size < (ssize_t) (8 * ngx_pagesize)) {
775 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
776 "zone \"%V\" is too small", &value[i]);
777 return NGX_CONF_ERROR;
780 continue;
783 if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
785 len = value[i].len;
786 p = value[i].data + len - 3;
788 if (ngx_strncmp(p, "r/s", 3) == 0) {
789 scale = 1;
790 len -= 3;
792 } else if (ngx_strncmp(p, "r/m", 3) == 0) {
793 scale = 60;
794 len -= 3;
797 rate = ngx_atoi(value[i].data + 5, len - 5);
798 if (rate <= 0) {
799 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
800 "invalid rate \"%V\"", &value[i]);
801 return NGX_CONF_ERROR;
804 continue;
807 if (value[i].data[0] == '$') {
809 value[i].len--;
810 value[i].data++;
812 ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));
813 if (ctx == NULL) {
814 return NGX_CONF_ERROR;
817 ctx->index = ngx_http_get_variable_index(cf, &value[i]);
818 if (ctx->index == NGX_ERROR) {
819 return NGX_CONF_ERROR;
822 ctx->var = value[i];
824 continue;
827 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
828 "invalid parameter \"%V\"", &value[i]);
829 return NGX_CONF_ERROR;
832 if (name.len == 0) {
833 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
834 "\"%V\" must have \"zone\" parameter",
835 &cmd->name);
836 return NGX_CONF_ERROR;
839 if (ctx == NULL) {
840 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
841 "no variable is defined for %V \"%V\"",
842 &cmd->name, &name);
843 return NGX_CONF_ERROR;
846 ctx->rate = rate * 1000 / scale;
848 shm_zone = ngx_shared_memory_add(cf, &name, size,
849 &ngx_http_limit_req_module);
850 if (shm_zone == NULL) {
851 return NGX_CONF_ERROR;
854 if (shm_zone->data) {
855 ctx = shm_zone->data;
857 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
858 "%V \"%V\" is already bound to variable \"%V\"",
859 &cmd->name, &name, &ctx->var);
860 return NGX_CONF_ERROR;
863 shm_zone->init = ngx_http_limit_req_init_zone;
864 shm_zone->data = ctx;
866 return NGX_CONF_OK;
870 static char *
871 ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
873 ngx_http_limit_req_conf_t *lrcf = conf;
875 ngx_int_t burst;
876 ngx_str_t *value, s;
877 ngx_uint_t i, nodelay;
878 ngx_shm_zone_t *shm_zone;
879 ngx_http_limit_req_limit_t *limit, *limits;
881 value = cf->args->elts;
883 shm_zone = NULL;
884 burst = 0;
885 nodelay = 0;
887 for (i = 1; i < cf->args->nelts; i++) {
889 if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
891 s.len = value[i].len - 5;
892 s.data = value[i].data + 5;
894 shm_zone = ngx_shared_memory_add(cf, &s, 0,
895 &ngx_http_limit_req_module);
896 if (shm_zone == NULL) {
897 return NGX_CONF_ERROR;
900 continue;
903 if (ngx_strncmp(value[i].data, "burst=", 6) == 0) {
905 burst = ngx_atoi(value[i].data + 6, value[i].len - 6);
906 if (burst <= 0) {
907 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
908 "invalid burst rate \"%V\"", &value[i]);
909 return NGX_CONF_ERROR;
912 continue;
915 if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) {
916 nodelay = 1;
917 continue;
920 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
921 "invalid parameter \"%V\"", &value[i]);
922 return NGX_CONF_ERROR;
925 if (shm_zone == NULL) {
926 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
927 "\"%V\" must have \"zone\" parameter",
928 &cmd->name);
929 return NGX_CONF_ERROR;
932 if (shm_zone->data == NULL) {
933 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
934 "unknown limit_req_zone \"%V\"",
935 &shm_zone->shm.name);
936 return NGX_CONF_ERROR;
939 limits = lrcf->limits.elts;
941 if (limits == NULL) {
942 if (ngx_array_init(&lrcf->limits, cf->pool, 1,
943 sizeof(ngx_http_limit_req_limit_t))
944 != NGX_OK)
946 return NGX_CONF_ERROR;
950 for (i = 0; i < lrcf->limits.nelts; i++) {
951 if (shm_zone == limits[i].shm_zone) {
952 return "is duplicate";
956 limit = ngx_array_push(&lrcf->limits);
957 if (limit == NULL) {
958 return NGX_CONF_ERROR;
961 limit->shm_zone = shm_zone;
962 limit->burst = burst * 1000;
963 limit->nodelay = nodelay;
965 return NGX_CONF_OK;
969 static ngx_int_t
970 ngx_http_limit_req_init(ngx_conf_t *cf)
972 ngx_http_handler_pt *h;
973 ngx_http_core_main_conf_t *cmcf;
975 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
977 h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
978 if (h == NULL) {
979 return NGX_ERROR;
982 *h = ngx_http_limit_req_handler;
984 return NGX_OK;