Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_limit_req_module.c
Go to the documentation of this file.
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11 
12 
13 typedef struct {
14  u_char color;
15  u_char dummy;
16  u_short len;
19  /* integer value, 1 corresponds to 0.001 r/s */
22  u_char data[1];
24 
25 
26 typedef struct {
31 
32 
33 typedef struct {
36  /* integer value, 1 corresponds to 0.001 r/s */
42 
43 
44 typedef struct {
46  /* integer value, 1 corresponds to 0.001 r/s */
48  ngx_uint_t nodelay; /* unsigned nodelay:1 */
50 
51 
52 typedef struct {
58 
59 
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,
66 static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
67  ngx_uint_t n);
68 
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);
77 
78 
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 }
85 };
86 
87 
88 static ngx_conf_num_bounds_t ngx_http_limit_req_status_bounds = {
90 };
91 
92 
93 static ngx_command_t ngx_http_limit_req_commands[] = {
94 
95  { ngx_string("limit_req_zone"),
97  ngx_http_limit_req_zone,
98  0,
99  0,
100  NULL },
101 
102  { ngx_string("limit_req"),
104  ngx_http_limit_req,
106  0,
107  NULL },
108 
109  { ngx_string("limit_req_log_level"),
113  offsetof(ngx_http_limit_req_conf_t, limit_log_level),
114  &ngx_http_limit_req_log_levels },
115 
116  { ngx_string("limit_req_status"),
120  offsetof(ngx_http_limit_req_conf_t, status_code),
121  &ngx_http_limit_req_status_bounds },
122 
124 };
125 
126 
127 static ngx_http_module_t ngx_http_limit_req_module_ctx = {
128  NULL, /* preconfiguration */
129  ngx_http_limit_req_init, /* postconfiguration */
130 
131  NULL, /* create main configuration */
132  NULL, /* init main configuration */
133 
134  NULL, /* create server configuration */
135  NULL, /* merge server configuration */
136 
137  ngx_http_limit_req_create_conf, /* create location configuration */
138  ngx_http_limit_req_merge_conf /* merge location configuration */
139 };
140 
141 
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 */
155 };
156 
157 
158 static ngx_int_t
159 ngx_http_limit_req_handler(ngx_http_request_t *r)
160 {
161  size_t len;
162  uint32_t hash;
163  ngx_int_t rc;
164  ngx_uint_t n, excess;
165  ngx_msec_t delay;
169  ngx_http_limit_req_limit_t *limit, *limits;
170 
171  if (r->main->limit_req_set) {
172  return NGX_DECLINED;
173  }
174 
175  lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
176  limits = lrcf->limits.elts;
177 
178  excess = 0;
179 
180  rc = NGX_DECLINED;
181 
182 #if (NGX_SUPPRESS_WARN)
183  limit = NULL;
184 #endif
185 
186  for (n = 0; n < lrcf->limits.nelts; n++) {
187 
188  limit = &limits[n];
189 
190  ctx = limit->shm_zone->data;
191 
192  vv = ngx_http_get_indexed_variable(r, ctx->index);
193 
194  if (vv == NULL || vv->not_found) {
195  continue;
196  }
197 
198  len = vv->len;
199 
200  if (len == 0) {
201  continue;
202  }
203 
204  if (len > 65535) {
206  "the value of the \"%V\" variable "
207  "is more than 65535 bytes: \"%v\"",
208  &ctx->var, vv);
209  continue;
210  }
211 
212  hash = ngx_crc32_short(vv->data, len);
213 
214  ngx_shmtx_lock(&ctx->shpool->mutex);
215 
216  rc = ngx_http_limit_req_lookup(limit, hash, vv->data, len, &excess,
217  (n == lrcf->limits.nelts - 1));
218 
219  ngx_shmtx_unlock(&ctx->shpool->mutex);
220 
222  "limit_req[%ui]: %i %ui.%03ui",
223  n, rc, excess / 1000, excess % 1000);
224 
225  if (rc != NGX_AGAIN) {
226  break;
227  }
228  }
229 
230  if (rc == NGX_DECLINED) {
231  return NGX_DECLINED;
232  }
233 
234  r->main->limit_req_set = 1;
235 
236  if (rc == NGX_BUSY || rc == NGX_ERROR) {
237 
238  if (rc == NGX_BUSY) {
240  "limiting requests, excess: %ui.%03ui by zone \"%V\"",
241  excess / 1000, excess % 1000,
242  &limit->shm_zone->shm.name);
243  }
244 
245  while (n--) {
246  ctx = limits[n].shm_zone->data;
247 
248  if (ctx->node == NULL) {
249  continue;
250  }
251 
252  ngx_shmtx_lock(&ctx->shpool->mutex);
253 
254  ctx->node->count--;
255 
256  ngx_shmtx_unlock(&ctx->shpool->mutex);
257 
258  ctx->node = NULL;
259  }
260 
261  return lrcf->status_code;
262  }
263 
264  /* rc == NGX_AGAIN || rc == NGX_OK */
265 
266  if (rc == NGX_AGAIN) {
267  excess = 0;
268  }
269 
270  delay = ngx_http_limit_req_account(limits, n, &excess, &limit);
271 
272  if (!delay) {
273  return NGX_DECLINED;
274  }
275 
277  "delaying request, excess: %ui.%03ui, by zone \"%V\"",
278  excess / 1000, excess % 1000, &limit->shm_zone->shm.name);
279 
280  if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
282  }
283 
285  r->write_event_handler = ngx_http_limit_req_delay;
286  ngx_add_timer(r->connection->write, delay);
287 
288  return NGX_AGAIN;
289 }
290 
291 
292 static void
293 ngx_http_limit_req_delay(ngx_http_request_t *r)
294 {
295  ngx_event_t *wev;
296 
298  "limit_req delay");
299 
300  wev = r->connection->write;
301 
302  if (!wev->timedout) {
303 
304  if (ngx_handle_write_event(wev, 0) != NGX_OK) {
306  }
307 
308  return;
309  }
310 
311  wev->timedout = 0;
312 
313  if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
315  return;
316  }
317 
320 
322 }
323 
324 
325 static void
326 ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp,
328 {
329  ngx_rbtree_node_t **p;
330  ngx_http_limit_req_node_t *lrn, *lrnt;
331 
332  for ( ;; ) {
333 
334  if (node->key < temp->key) {
335 
336  p = &temp->left;
337 
338  } else if (node->key > temp->key) {
339 
340  p = &temp->right;
341 
342  } else { /* node->key == temp->key */
343 
344  lrn = (ngx_http_limit_req_node_t *) &node->color;
345  lrnt = (ngx_http_limit_req_node_t *) &temp->color;
346 
347  p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)
348  ? &temp->left : &temp->right;
349  }
350 
351  if (*p == sentinel) {
352  break;
353  }
354 
355  temp = *p;
356  }
357 
358  *p = node;
359  node->parent = temp;
360  node->left = sentinel;
361  node->right = sentinel;
362  ngx_rbt_red(node);
363 }
364 
365 
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)
369 {
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;
378 
379  tp = ngx_timeofday();
380  now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
381 
382  ctx = limit->shm_zone->data;
383 
384  node = ctx->sh->rbtree.root;
385  sentinel = ctx->sh->rbtree.sentinel;
386 
387  while (node != sentinel) {
388 
389  if (hash < node->key) {
390  node = node->left;
391  continue;
392  }
393 
394  if (hash > node->key) {
395  node = node->right;
396  continue;
397  }
398 
399  /* hash == node->key */
400 
401  lr = (ngx_http_limit_req_node_t *) &node->color;
402 
403  rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len);
404 
405  if (rc == 0) {
406  ngx_queue_remove(&lr->queue);
407  ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
408 
409  ms = (ngx_msec_int_t) (now - lr->last);
410 
411  excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
412 
413  if (excess < 0) {
414  excess = 0;
415  }
416 
417  *ep = excess;
418 
419  if ((ngx_uint_t) excess > limit->burst) {
420  return NGX_BUSY;
421  }
422 
423  if (account) {
424  lr->excess = excess;
425  lr->last = now;
426  return NGX_OK;
427  }
428 
429  lr->count++;
430 
431  ctx->node = lr;
432 
433  return NGX_AGAIN;
434  }
435 
436  node = (rc < 0) ? node->left : node->right;
437  }
438 
439  *ep = 0;
440 
441  size = offsetof(ngx_rbtree_node_t, color)
442  + offsetof(ngx_http_limit_req_node_t, data)
443  + len;
444 
445  ngx_http_limit_req_expire(ctx, 1);
446 
447  node = ngx_slab_alloc_locked(ctx->shpool, size);
448 
449  if (node == NULL) {
450  ngx_http_limit_req_expire(ctx, 0);
451 
452  node = ngx_slab_alloc_locked(ctx->shpool, size);
453  if (node == NULL) {
454  return NGX_ERROR;
455  }
456  }
457 
458  node->key = hash;
459 
460  lr = (ngx_http_limit_req_node_t *) &node->color;
461 
462  lr->len = (u_char) len;
463  lr->excess = 0;
464 
465  ngx_memcpy(lr->data, data, len);
466 
467  ngx_rbtree_insert(&ctx->sh->rbtree, node);
468 
469  ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
470 
471  if (account) {
472  lr->last = now;
473  lr->count = 0;
474  return NGX_OK;
475  }
476 
477  lr->last = 0;
478  lr->count = 1;
479 
480  ctx->node = lr;
481 
482  return NGX_AGAIN;
483 }
484 
485 
486 static ngx_msec_t
487 ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n,
489 {
490  ngx_int_t excess;
491  ngx_time_t *tp;
492  ngx_msec_t now, delay, max_delay;
493  ngx_msec_int_t ms;
496 
497  excess = *ep;
498 
499  if (excess == 0 || (*limit)->nodelay) {
500  max_delay = 0;
501 
502  } else {
503  ctx = (*limit)->shm_zone->data;
504  max_delay = excess * 1000 / ctx->rate;
505  }
506 
507  while (n--) {
508  ctx = limits[n].shm_zone->data;
509  lr = ctx->node;
510 
511  if (lr == NULL) {
512  continue;
513  }
514 
515  ngx_shmtx_lock(&ctx->shpool->mutex);
516 
517  tp = ngx_timeofday();
518 
519  now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
520  ms = (ngx_msec_int_t) (now - lr->last);
521 
522  excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
523 
524  if (excess < 0) {
525  excess = 0;
526  }
527 
528  lr->last = now;
529  lr->excess = excess;
530  lr->count--;
531 
532  ngx_shmtx_unlock(&ctx->shpool->mutex);
533 
534  ctx->node = NULL;
535 
536  if (limits[n].nodelay) {
537  continue;
538  }
539 
540  delay = excess * 1000 / ctx->rate;
541 
542  if (delay > max_delay) {
543  max_delay = delay;
544  *ep = excess;
545  *limit = &limits[n];
546  }
547  }
548 
549  return max_delay;
550 }
551 
552 
553 static void
554 ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
555 {
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;
563 
564  tp = ngx_timeofday();
565 
566  now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
567 
568  /*
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
572  */
573 
574  while (n < 3) {
575 
576  if (ngx_queue_empty(&ctx->sh->queue)) {
577  return;
578  }
579 
580  q = ngx_queue_last(&ctx->sh->queue);
581 
583 
584  if (lr->count) {
585 
586  /*
587  * There is not much sense in looking further,
588  * because we bump nodes on the lookup stage.
589  */
590 
591  return;
592  }
593 
594  if (n++ != 0) {
595 
596  ms = (ngx_msec_int_t) (now - lr->last);
597  ms = ngx_abs(ms);
598 
599  if (ms < 60000) {
600  return;
601  }
602 
603  excess = lr->excess - ctx->rate * ms / 1000;
604 
605  if (excess > 0) {
606  return;
607  }
608  }
609 
610  ngx_queue_remove(q);
611 
612  node = (ngx_rbtree_node_t *)
613  ((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
614 
615  ngx_rbtree_delete(&ctx->sh->rbtree, node);
616 
617  ngx_slab_free_locked(ctx->shpool, node);
618  }
619 }
620 
621 
622 static ngx_int_t
623 ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)
624 {
625  ngx_http_limit_req_ctx_t *octx = data;
626 
627  size_t len;
629 
630  ctx = shm_zone->data;
631 
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;
639  }
640 
641  ctx->sh = octx->sh;
642  ctx->shpool = octx->shpool;
643 
644  return NGX_OK;
645  }
646 
647  ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
648 
649  if (shm_zone->shm.exists) {
650  ctx->sh = ctx->shpool->data;
651 
652  return NGX_OK;
653  }
654 
655  ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t));
656  if (ctx->sh == NULL) {
657  return NGX_ERROR;
658  }
659 
660  ctx->shpool->data = ctx->sh;
661 
662  ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
663  ngx_http_limit_req_rbtree_insert_value);
664 
665  ngx_queue_init(&ctx->sh->queue);
666 
667  len = sizeof(" in limit_req zone \"\"") + shm_zone->shm.name.len;
668 
669  ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
670  if (ctx->shpool->log_ctx == NULL) {
671  return NGX_ERROR;
672  }
673 
674  ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",
675  &shm_zone->shm.name);
676 
677  return NGX_OK;
678 }
679 
680 
681 static void *
682 ngx_http_limit_req_create_conf(ngx_conf_t *cf)
683 {
685 
686  conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));
687  if (conf == NULL) {
688  return NULL;
689  }
690 
691  /*
692  * set by ngx_pcalloc():
693  *
694  * conf->limits.elts = NULL;
695  */
696 
699 
700  return conf;
701 }
702 
703 
704 static char *
705 ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
706 {
707  ngx_http_limit_req_conf_t *prev = parent;
708  ngx_http_limit_req_conf_t *conf = child;
709 
710  if (conf->limits.elts == NULL) {
711  conf->limits = prev->limits;
712  }
713 
715  NGX_LOG_ERR);
716 
717  conf->delay_log_level = (conf->limit_log_level == NGX_LOG_INFO) ?
718  NGX_LOG_INFO : conf->limit_log_level + 1;
719 
722 
723  return NGX_CONF_OK;
724 }
725 
726 
727 static char *
728 ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
729 {
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;
738 
739  value = cf->args->elts;
740 
741  ctx = NULL;
742  size = 0;
743  rate = 1;
744  scale = 1;
745  name.len = 0;
746 
747  for (i = 1; i < cf->args->nelts; i++) {
748 
749  if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
750 
751  name.data = value[i].data + 5;
752 
753  p = (u_char *) ngx_strchr(name.data, ':');
754 
755  if (p == NULL) {
757  "invalid zone size \"%V\"", &value[i]);
758  return NGX_CONF_ERROR;
759  }
760 
761  name.len = p - name.data;
762 
763  s.data = p + 1;
764  s.len = value[i].data + value[i].len - s.data;
765 
766  size = ngx_parse_size(&s);
767 
768  if (size == NGX_ERROR) {
770  "invalid zone size \"%V\"", &value[i]);
771  return NGX_CONF_ERROR;
772  }
773 
774  if (size < (ssize_t) (8 * ngx_pagesize)) {
776  "zone \"%V\" is too small", &value[i]);
777  return NGX_CONF_ERROR;
778  }
779 
780  continue;
781  }
782 
783  if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
784 
785  len = value[i].len;
786  p = value[i].data + len - 3;
787 
788  if (ngx_strncmp(p, "r/s", 3) == 0) {
789  scale = 1;
790  len -= 3;
791 
792  } else if (ngx_strncmp(p, "r/m", 3) == 0) {
793  scale = 60;
794  len -= 3;
795  }
796 
797  rate = ngx_atoi(value[i].data + 5, len - 5);
798  if (rate <= 0) {
800  "invalid rate \"%V\"", &value[i]);
801  return NGX_CONF_ERROR;
802  }
803 
804  continue;
805  }
806 
807  if (value[i].data[0] == '$') {
808 
809  value[i].len--;
810  value[i].data++;
811 
812  ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));
813  if (ctx == NULL) {
814  return NGX_CONF_ERROR;
815  }
816 
817  ctx->index = ngx_http_get_variable_index(cf, &value[i]);
818  if (ctx->index == NGX_ERROR) {
819  return NGX_CONF_ERROR;
820  }
821 
822  ctx->var = value[i];
823 
824  continue;
825  }
826 
828  "invalid parameter \"%V\"", &value[i]);
829  return NGX_CONF_ERROR;
830  }
831 
832  if (name.len == 0) {
834  "\"%V\" must have \"zone\" parameter",
835  &cmd->name);
836  return NGX_CONF_ERROR;
837  }
838 
839  if (ctx == NULL) {
841  "no variable is defined for %V \"%V\"",
842  &cmd->name, &name);
843  return NGX_CONF_ERROR;
844  }
845 
846  ctx->rate = rate * 1000 / scale;
847 
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;
852  }
853 
854  if (shm_zone->data) {
855  ctx = shm_zone->data;
856 
858  "%V \"%V\" is already bound to variable \"%V\"",
859  &cmd->name, &name, &ctx->var);
860  return NGX_CONF_ERROR;
861  }
862 
863  shm_zone->init = ngx_http_limit_req_init_zone;
864  shm_zone->data = ctx;
865 
866  return NGX_CONF_OK;
867 }
868 
869 
870 static char *
871 ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
872 {
873  ngx_http_limit_req_conf_t *lrcf = conf;
874 
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;
880 
881  value = cf->args->elts;
882 
883  shm_zone = NULL;
884  burst = 0;
885  nodelay = 0;
886 
887  for (i = 1; i < cf->args->nelts; i++) {
888 
889  if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
890 
891  s.len = value[i].len - 5;
892  s.data = value[i].data + 5;
893 
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;
898  }
899 
900  continue;
901  }
902 
903  if (ngx_strncmp(value[i].data, "burst=", 6) == 0) {
904 
905  burst = ngx_atoi(value[i].data + 6, value[i].len - 6);
906  if (burst <= 0) {
908  "invalid burst rate \"%V\"", &value[i]);
909  return NGX_CONF_ERROR;
910  }
911 
912  continue;
913  }
914 
915  if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) {
916  nodelay = 1;
917  continue;
918  }
919 
921  "invalid parameter \"%V\"", &value[i]);
922  return NGX_CONF_ERROR;
923  }
924 
925  if (shm_zone == NULL) {
927  "\"%V\" must have \"zone\" parameter",
928  &cmd->name);
929  return NGX_CONF_ERROR;
930  }
931 
932  if (shm_zone->data == NULL) {
934  "unknown limit_req_zone \"%V\"",
935  &shm_zone->shm.name);
936  return NGX_CONF_ERROR;
937  }
938 
939  limits = lrcf->limits.elts;
940 
941  if (limits == NULL) {
942  if (ngx_array_init(&lrcf->limits, cf->pool, 1,
944  != NGX_OK)
945  {
946  return NGX_CONF_ERROR;
947  }
948  }
949 
950  for (i = 0; i < lrcf->limits.nelts; i++) {
951  if (shm_zone == limits[i].shm_zone) {
952  return "is duplicate";
953  }
954  }
955 
956  limit = ngx_array_push(&lrcf->limits);
957  if (limit == NULL) {
958  return NGX_CONF_ERROR;
959  }
960 
961  limit->shm_zone = shm_zone;
962  limit->burst = burst * 1000;
963  limit->nodelay = nodelay;
964 
965  return NGX_CONF_OK;
966 }
967 
968 
969 static ngx_int_t
970 ngx_http_limit_req_init(ngx_conf_t *cf)
971 {
974 
976 
978  if (h == NULL) {
979  return NGX_ERROR;
980  }
981 
982  *h = ngx_http_limit_req_handler;
983 
984  return NGX_OK;
985 }