3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
19 /* integer value, 1 corresponds to 0.001 r/s */
23 } ngx_http_limit_req_node_t
;
28 ngx_rbtree_node_t sentinel
;
30 } ngx_http_limit_req_shctx_t
;
34 ngx_http_limit_req_shctx_t
*sh
;
35 ngx_slab_pool_t
*shpool
;
36 /* integer value, 1 corresponds to 0.001 r/s */
40 ngx_http_limit_req_node_t
*node
;
41 } ngx_http_limit_req_ctx_t
;
45 ngx_shm_zone_t
*shm_zone
;
46 /* integer value, 1 corresponds to 0.001 r/s */
48 ngx_uint_t nodelay
; /* unsigned nodelay:1 */
49 } ngx_http_limit_req_limit_t
;
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
,
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
,
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
,
72 static char *ngx_http_limit_req_zone(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
74 static char *ngx_http_limit_req(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
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
,
102 { ngx_string("limit_req"),
103 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_TAKE123
,
105 NGX_HTTP_LOC_CONF_OFFSET
,
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
},
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
= {
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
159 ngx_http_limit_req_handler(ngx_http_request_t
*r
)
164 ngx_uint_t n
, excess
;
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
) {
175 lrcf
= ngx_http_get_module_loc_conf(r
, ngx_http_limit_req_module
);
176 limits
= lrcf
->limits
.elts
;
182 #if (NGX_SUPPRESS_WARN)
186 for (n
= 0; n
< lrcf
->limits
.nelts
; n
++) {
190 ctx
= limit
->shm_zone
->data
;
192 vv
= ngx_http_get_indexed_variable(r
, ctx
->index
);
194 if (vv
== NULL
|| vv
->not_found
) {
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\"",
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
) {
230 if (rc
== 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
);
246 ctx
= limits
[n
].shm_zone
->data
;
248 if (ctx
->node
== NULL
) {
252 ngx_shmtx_lock(&ctx
->shpool
->mutex
);
256 ngx_shmtx_unlock(&ctx
->shpool
->mutex
);
261 return lrcf
->status_code
;
264 /* rc == NGX_AGAIN || rc == NGX_OK */
266 if (rc
== NGX_AGAIN
) {
270 delay
= ngx_http_limit_req_account(limits
, n
, &excess
, &limit
);
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
);
293 ngx_http_limit_req_delay(ngx_http_request_t
*r
)
297 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
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
);
313 if (ngx_handle_read_event(r
->connection
->read
, 0) != NGX_OK
) {
314 ngx_http_finalize_request(r
, NGX_HTTP_INTERNAL_SERVER_ERROR
);
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
);
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
;
334 if (node
->key
< temp
->key
) {
338 } else if (node
->key
> temp
->key
) {
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
) {
360 node
->left
= sentinel
;
361 node
->right
= sentinel
;
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
)
371 ngx_int_t rc
, excess
;
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
) {
394 if (hash
> node
->key
) {
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
);
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;
419 if ((ngx_uint_t
) excess
> limit
->burst
) {
436 node
= (rc
< 0) ? node
->left
: node
->right
;
441 size
= offsetof(ngx_rbtree_node_t
, color
)
442 + offsetof(ngx_http_limit_req_node_t
, data
)
445 ngx_http_limit_req_expire(ctx
, 1);
447 node
= ngx_slab_alloc_locked(ctx
->shpool
, size
);
450 ngx_http_limit_req_expire(ctx
, 0);
452 node
= ngx_slab_alloc_locked(ctx
->shpool
, size
);
460 lr
= (ngx_http_limit_req_node_t
*) &node
->color
;
462 lr
->len
= (u_char
) len
;
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
);
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
)
492 ngx_msec_t now
, delay
, max_delay
;
494 ngx_http_limit_req_ctx_t
*ctx
;
495 ngx_http_limit_req_node_t
*lr
;
499 if (excess
== 0 || (*limit
)->nodelay
) {
503 ctx
= (*limit
)->shm_zone
->data
;
504 max_delay
= excess
* 1000 / ctx
->rate
;
508 ctx
= limits
[n
].shm_zone
->data
;
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;
532 ngx_shmtx_unlock(&ctx
->shpool
->mutex
);
536 if (limits
[n
].nodelay
) {
540 delay
= excess
* 1000 / ctx
->rate
;
542 if (delay
> max_delay
) {
554 ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t
*ctx
, ngx_uint_t n
)
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
576 if (ngx_queue_empty(&ctx
->sh
->queue
)) {
580 q
= ngx_queue_last(&ctx
->sh
->queue
);
582 lr
= ngx_queue_data(q
, ngx_http_limit_req_node_t
, queue
);
587 * There is not much sense in looking further,
588 * because we bump nodes on the lookup stage.
596 ms
= (ngx_msec_int_t
) (now
- lr
->last
);
603 excess
= lr
->excess
- ctx
->rate
* ms
/ 1000;
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
);
623 ngx_http_limit_req_init_zone(ngx_shm_zone_t
*shm_zone
, void *data
)
625 ngx_http_limit_req_ctx_t
*octx
= data
;
628 ngx_http_limit_req_ctx_t
*ctx
;
630 ctx
= shm_zone
->data
;
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
);
642 ctx
->shpool
= octx
->shpool
;
647 ctx
->shpool
= (ngx_slab_pool_t
*) shm_zone
->shm
.addr
;
649 if (shm_zone
->shm
.exists
) {
650 ctx
->sh
= ctx
->shpool
->data
;
655 ctx
->sh
= ngx_slab_alloc(ctx
->shpool
, sizeof(ngx_http_limit_req_shctx_t
));
656 if (ctx
->sh
== NULL
) {
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
) {
674 ngx_sprintf(ctx
->shpool
->log_ctx
, " in limit_req zone \"%V\"%Z",
675 &shm_zone
->shm
.name
);
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
));
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
;
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
,
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
);
728 ngx_http_limit_req_zone(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
733 ngx_str_t
*value
, name
, s
;
734 ngx_int_t rate
, scale
;
736 ngx_shm_zone_t
*shm_zone
;
737 ngx_http_limit_req_ctx_t
*ctx
;
739 value
= cf
->args
->elts
;
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
, ':');
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
;
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
;
783 if (ngx_strncmp(value
[i
].data
, "rate=", 5) == 0) {
786 p
= value
[i
].data
+ len
- 3;
788 if (ngx_strncmp(p
, "r/s", 3) == 0) {
792 } else if (ngx_strncmp(p
, "r/m", 3) == 0) {
797 rate
= ngx_atoi(value
[i
].data
+ 5, len
- 5);
799 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
800 "invalid rate \"%V\"", &value
[i
]);
801 return NGX_CONF_ERROR
;
807 if (value
[i
].data
[0] == '$') {
812 ctx
= ngx_pcalloc(cf
->pool
, sizeof(ngx_http_limit_req_ctx_t
));
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
;
827 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
828 "invalid parameter \"%V\"", &value
[i
]);
829 return NGX_CONF_ERROR
;
833 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
834 "\"%V\" must have \"zone\" parameter",
836 return NGX_CONF_ERROR
;
840 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
841 "no variable is defined for %V \"%V\"",
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
;
871 ngx_http_limit_req(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
873 ngx_http_limit_req_conf_t
*lrcf
= conf
;
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
;
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
;
903 if (ngx_strncmp(value
[i
].data
, "burst=", 6) == 0) {
905 burst
= ngx_atoi(value
[i
].data
+ 6, value
[i
].len
- 6);
907 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
908 "invalid burst rate \"%V\"", &value
[i
]);
909 return NGX_CONF_ERROR
;
915 if (ngx_strncmp(value
[i
].data
, "nodelay", 7) == 0) {
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",
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
))
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
);
958 return NGX_CONF_ERROR
;
961 limit
->shm_zone
= shm_zone
;
962 limit
->burst
= burst
* 1000;
963 limit
->nodelay
= nodelay
;
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
);
982 *h
= ngx_http_limit_req_handler
;