comparison src/http/modules/ngx_http_limit_req_module.c @ 660:d0f7a625f27c NGINX_1_1_14

nginx 1.1.14 *) Feature: multiple "limit_req" limits may be used simultaneously. *) Bugfix: in error handling while connecting to a backend. Thanks to Piotr Sikora. *) Bugfix: in AIO error handling on FreeBSD. *) Bugfix: in the OpenSSL library initialization. *) Bugfix: the "proxy_redirect" directives might not be correctly inherited. *) Bugfix: memory leak during reconfiguration if the "pcre_jit" directive was used.
author Igor Sysoev <http://sysoev.ru>
date Mon, 30 Jan 2012 00:00:00 +0400
parents 5a4401b9551b
children f5b859b2f097
comparison
equal deleted inserted replaced
659:d48f991d7bd0 660:d0f7a625f27c
1 1
2 /* 2 /*
3 * Copyright (C) Igor Sysoev 3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
4 */ 5 */
5 6
6 7
7 #include <ngx_config.h> 8 #include <ngx_config.h>
8 #include <ngx_core.h> 9 #include <ngx_core.h>
15 u_short len; 16 u_short len;
16 ngx_queue_t queue; 17 ngx_queue_t queue;
17 ngx_msec_t last; 18 ngx_msec_t last;
18 /* integer value, 1 corresponds to 0.001 r/s */ 19 /* integer value, 1 corresponds to 0.001 r/s */
19 ngx_uint_t excess; 20 ngx_uint_t excess;
21 ngx_uint_t count;
20 u_char data[1]; 22 u_char data[1];
21 } ngx_http_limit_req_node_t; 23 } ngx_http_limit_req_node_t;
22 24
23 25
24 typedef struct { 26 typedef struct {
33 ngx_slab_pool_t *shpool; 35 ngx_slab_pool_t *shpool;
34 /* integer value, 1 corresponds to 0.001 r/s */ 36 /* integer value, 1 corresponds to 0.001 r/s */
35 ngx_uint_t rate; 37 ngx_uint_t rate;
36 ngx_int_t index; 38 ngx_int_t index;
37 ngx_str_t var; 39 ngx_str_t var;
40 ngx_http_limit_req_node_t *node;
38 } ngx_http_limit_req_ctx_t; 41 } ngx_http_limit_req_ctx_t;
39 42
40 43
41 typedef struct { 44 typedef struct {
42 ngx_shm_zone_t *shm_zone; 45 ngx_shm_zone_t *shm_zone;
43 /* integer value, 1 corresponds to 0.001 r/s */ 46 /* integer value, 1 corresponds to 0.001 r/s */
44 ngx_uint_t burst; 47 ngx_uint_t burst;
48 ngx_uint_t nodelay; /* unsigned nodelay:1 */
49 } ngx_http_limit_req_limit_t;
50
51
52 typedef struct {
53 ngx_array_t limits;
45 ngx_uint_t limit_log_level; 54 ngx_uint_t limit_log_level;
46 ngx_uint_t delay_log_level; 55 ngx_uint_t delay_log_level;
47
48 ngx_uint_t nodelay; /* unsigned nodelay:1 */
49 } ngx_http_limit_req_conf_t; 56 } ngx_http_limit_req_conf_t;
50 57
51 58
52 static void ngx_http_limit_req_delay(ngx_http_request_t *r); 59 static void ngx_http_limit_req_delay(ngx_http_request_t *r);
53 static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, 60 static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit,
54 ngx_uint_t hash, u_char *data, size_t len, ngx_uint_t *ep); 61 ngx_uint_t hash, u_char *data, size_t len, ngx_uint_t *ep,
62 ngx_uint_t account);
63 static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits,
64 ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit);
55 static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, 65 static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
56 ngx_uint_t n); 66 ngx_uint_t n);
57 67
58 static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf); 68 static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf);
59 static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, 69 static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent,
133 143
134 144
135 static ngx_int_t 145 static ngx_int_t
136 ngx_http_limit_req_handler(ngx_http_request_t *r) 146 ngx_http_limit_req_handler(ngx_http_request_t *r)
137 { 147 {
138 size_t len, n; 148 size_t len;
139 uint32_t hash; 149 uint32_t hash;
140 ngx_int_t rc; 150 ngx_int_t rc;
141 ngx_uint_t excess; 151 ngx_uint_t n, excess;
142 ngx_time_t *tp; 152 ngx_msec_t delay;
143 ngx_rbtree_node_t *node; 153 ngx_http_variable_value_t *vv;
144 ngx_http_variable_value_t *vv; 154 ngx_http_limit_req_ctx_t *ctx;
145 ngx_http_limit_req_ctx_t *ctx; 155 ngx_http_limit_req_conf_t *lrcf;
146 ngx_http_limit_req_node_t *lr; 156 ngx_http_limit_req_limit_t *limit, *limits;
147 ngx_http_limit_req_conf_t *lrcf;
148 157
149 if (r->main->limit_req_set) { 158 if (r->main->limit_req_set) {
150 return NGX_DECLINED; 159 return NGX_DECLINED;
151 } 160 }
152 161
153 lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); 162 lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
154 163 limits = lrcf->limits.elts;
155 if (lrcf->shm_zone == NULL) { 164
165 excess = 0;
166
167 rc = NGX_DECLINED;
168
169 #if (NGX_SUPPRESS_WARN)
170 limit = NULL;
171 #endif
172
173 for (n = 0; n < lrcf->limits.nelts; n++) {
174
175 limit = &limits[n];
176
177 ctx = limit->shm_zone->data;
178
179 vv = ngx_http_get_indexed_variable(r, ctx->index);
180
181 if (vv == NULL || vv->not_found) {
182 continue;
183 }
184
185 len = vv->len;
186
187 if (len == 0) {
188 continue;
189 }
190
191 if (len > 65535) {
192 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
193 "the value of the \"%V\" variable "
194 "is more than 65535 bytes: \"%v\"",
195 &ctx->var, vv);
196 continue;
197 }
198
199 hash = ngx_crc32_short(vv->data, len);
200
201 ngx_shmtx_lock(&ctx->shpool->mutex);
202
203 rc = ngx_http_limit_req_lookup(limit, hash, vv->data, len, &excess,
204 (n == lrcf->limits.nelts - 1));
205
206 ngx_shmtx_unlock(&ctx->shpool->mutex);
207
208 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
209 "limit_req[%ui]: %i %ui.%03ui",
210 n, rc, excess / 1000, excess % 1000);
211
212 if (rc != NGX_AGAIN) {
213 break;
214 }
215 }
216
217 if (rc == NGX_DECLINED) {
156 return NGX_DECLINED; 218 return NGX_DECLINED;
157 } 219 }
158 220
159 ctx = lrcf->shm_zone->data;
160
161 vv = ngx_http_get_indexed_variable(r, ctx->index);
162
163 if (vv == NULL || vv->not_found) {
164 return NGX_DECLINED;
165 }
166
167 len = vv->len;
168
169 if (len == 0) {
170 return NGX_DECLINED;
171 }
172
173 if (len > 65535) {
174 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
175 "the value of the \"%V\" variable "
176 "is more than 65535 bytes: \"%v\"",
177 &ctx->var, vv);
178 return NGX_DECLINED;
179 }
180
181 r->main->limit_req_set = 1; 221 r->main->limit_req_set = 1;
182 222
183 hash = ngx_crc32_short(vv->data, len); 223 if (rc == NGX_BUSY || rc == NGX_ERROR) {
184 224
185 ngx_shmtx_lock(&ctx->shpool->mutex); 225 if (rc == NGX_BUSY) {
186 226 ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
187 ngx_http_limit_req_expire(ctx, 1); 227 "limiting requests, excess: %ui.%03ui by zone \"%V\"",
188 228 excess / 1000, excess % 1000,
189 rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &excess); 229 &limit->shm_zone->shm.name);
190 230 }
191 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 231
192 "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000); 232 while (n--) {
193 233 ctx = limits[n].shm_zone->data;
194 if (rc == NGX_DECLINED) { 234
195 235 if (ctx->node == NULL) {
196 n = offsetof(ngx_rbtree_node_t, color) 236 continue;
197 + offsetof(ngx_http_limit_req_node_t, data) 237 }
198 + len; 238
199 239 ngx_shmtx_lock(&ctx->shpool->mutex);
200 node = ngx_slab_alloc_locked(ctx->shpool, n); 240
201 if (node == NULL) { 241 ctx->node->count--;
202 242
203 ngx_http_limit_req_expire(ctx, 0); 243 ngx_shmtx_unlock(&ctx->shpool->mutex);
204 244
205 node = ngx_slab_alloc_locked(ctx->shpool, n); 245 ctx->node = NULL;
206 if (node == NULL) { 246 }
207 ngx_shmtx_unlock(&ctx->shpool->mutex);
208 return NGX_HTTP_SERVICE_UNAVAILABLE;
209 }
210 }
211
212 lr = (ngx_http_limit_req_node_t *) &node->color;
213
214 node->key = hash;
215 lr->len = (u_char) len;
216
217 tp = ngx_timeofday();
218 lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
219
220 lr->excess = 0;
221 ngx_memcpy(lr->data, vv->data, len);
222
223 ngx_rbtree_insert(&ctx->sh->rbtree, node);
224
225 ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
226
227 ngx_shmtx_unlock(&ctx->shpool->mutex);
228
229 return NGX_DECLINED;
230 }
231
232 ngx_shmtx_unlock(&ctx->shpool->mutex);
233
234 if (rc == NGX_OK) {
235 return NGX_DECLINED;
236 }
237
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, &lrcf->shm_zone->shm.name);
242 247
243 return NGX_HTTP_SERVICE_UNAVAILABLE; 248 return NGX_HTTP_SERVICE_UNAVAILABLE;
244 } 249 }
245 250
246 /* rc == NGX_AGAIN */ 251 /* rc == NGX_AGAIN || rc == NGX_OK */
247 252
248 if (lrcf->nodelay) { 253 if (rc == NGX_AGAIN) {
254 excess = 0;
255 }
256
257 delay = ngx_http_limit_req_account(limits, n, &excess, &limit);
258
259 if (!delay) {
249 return NGX_DECLINED; 260 return NGX_DECLINED;
250 } 261 }
251 262
252 ngx_log_error(lrcf->delay_log_level, r->connection->log, 0, 263 ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
253 "delaying request, excess: %ui.%03ui, by zone \"%V\"", 264 "delaying request, excess: %ui.%03ui, by zone \"%V\"",
254 excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name); 265 excess / 1000, excess % 1000, &limit->shm_zone->shm.name);
255 266
256 if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { 267 if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
257 return NGX_HTTP_INTERNAL_SERVER_ERROR; 268 return NGX_HTTP_INTERNAL_SERVER_ERROR;
258 } 269 }
259 270
260 r->read_event_handler = ngx_http_test_reading; 271 r->read_event_handler = ngx_http_test_reading;
261 r->write_event_handler = ngx_http_limit_req_delay; 272 r->write_event_handler = ngx_http_limit_req_delay;
262 ngx_add_timer(r->connection->write, 273 ngx_add_timer(r->connection->write, delay);
263 (ngx_msec_t) excess * 1000 / ctx->rate);
264 274
265 return NGX_AGAIN; 275 return NGX_AGAIN;
266 } 276 }
267 277
268 278
339 ngx_rbt_red(node); 349 ngx_rbt_red(node);
340 } 350 }
341 351
342 352
343 static ngx_int_t 353 static ngx_int_t
344 ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash, 354 ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash,
345 u_char *data, size_t len, ngx_uint_t *ep) 355 u_char *data, size_t len, ngx_uint_t *ep, ngx_uint_t account)
346 { 356 {
357 size_t size;
347 ngx_int_t rc, excess; 358 ngx_int_t rc, excess;
348 ngx_time_t *tp; 359 ngx_time_t *tp;
349 ngx_msec_t now; 360 ngx_msec_t now;
350 ngx_msec_int_t ms; 361 ngx_msec_int_t ms;
351 ngx_rbtree_node_t *node, *sentinel; 362 ngx_rbtree_node_t *node, *sentinel;
352 ngx_http_limit_req_ctx_t *ctx; 363 ngx_http_limit_req_ctx_t *ctx;
353 ngx_http_limit_req_node_t *lr; 364 ngx_http_limit_req_node_t *lr;
354 365
355 ctx = lrcf->shm_zone->data; 366 tp = ngx_timeofday();
367 now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
368
369 ctx = limit->shm_zone->data;
356 370
357 node = ctx->sh->rbtree.root; 371 node = ctx->sh->rbtree.root;
358 sentinel = ctx->sh->rbtree.sentinel; 372 sentinel = ctx->sh->rbtree.sentinel;
359 373
360 while (node != sentinel) { 374 while (node != sentinel) {
378 392
379 if (rc == 0) { 393 if (rc == 0) {
380 ngx_queue_remove(&lr->queue); 394 ngx_queue_remove(&lr->queue);
381 ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); 395 ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
382 396
383 tp = ngx_timeofday();
384
385 now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
386 ms = (ngx_msec_int_t) (now - lr->last); 397 ms = (ngx_msec_int_t) (now - lr->last);
387 398
388 excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; 399 excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
389 400
390 if (excess < 0) { 401 if (excess < 0) {
391 excess = 0; 402 excess = 0;
392 } 403 }
393 404
394 *ep = excess; 405 *ep = excess;
395 406
396 if ((ngx_uint_t) excess > lrcf->burst) { 407 if ((ngx_uint_t) excess > limit->burst) {
397 return NGX_BUSY; 408 return NGX_BUSY;
398 } 409 }
399 410
400 lr->excess = excess; 411 if (account) {
401 lr->last = now; 412 lr->excess = excess;
402 413 lr->last = now;
403 if (excess) { 414 return NGX_OK;
404 return NGX_AGAIN;
405 } 415 }
406 416
407 return NGX_OK; 417 lr->count++;
418
419 ctx->node = lr;
420
421 return NGX_AGAIN;
408 } 422 }
409 423
410 node = (rc < 0) ? node->left : node->right; 424 node = (rc < 0) ? node->left : node->right;
411 425
412 } while (node != sentinel && hash == node->key); 426 } while (node != sentinel && hash == node->key);
414 break; 428 break;
415 } 429 }
416 430
417 *ep = 0; 431 *ep = 0;
418 432
419 return NGX_DECLINED; 433 size = offsetof(ngx_rbtree_node_t, color)
434 + offsetof(ngx_http_limit_req_node_t, data)
435 + len;
436
437 ngx_http_limit_req_expire(ctx, 1);
438
439 node = ngx_slab_alloc_locked(ctx->shpool, size);
440
441 if (node == NULL) {
442 ngx_http_limit_req_expire(ctx, 0);
443
444 node = ngx_slab_alloc_locked(ctx->shpool, size);
445 if (node == NULL) {
446 return NGX_ERROR;
447 }
448 }
449
450 node->key = hash;
451
452 ngx_rbtree_insert(&ctx->sh->rbtree, node);
453
454 lr = (ngx_http_limit_req_node_t *) &node->color;
455
456 ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
457
458 lr->len = (u_char) len;
459 lr->excess = 0;
460
461 ngx_memcpy(lr->data, data, len);
462
463 if (account) {
464 lr->last = now;
465 lr->count = 0;
466 return NGX_OK;
467 }
468
469 lr->last = 0;
470 lr->count = 1;
471
472 ctx->node = lr;
473
474 return NGX_AGAIN;
475 }
476
477
478 static ngx_msec_t
479 ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n,
480 ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit)
481 {
482 ngx_int_t excess;
483 ngx_time_t *tp;
484 ngx_msec_t now, delay, max_delay;
485 ngx_msec_int_t ms;
486 ngx_http_limit_req_ctx_t *ctx;
487 ngx_http_limit_req_node_t *lr;
488
489 excess = *ep;
490
491 if (excess == 0 || (*limit)->nodelay) {
492 max_delay = 0;
493
494 } else {
495 ctx = (*limit)->shm_zone->data;
496 max_delay = excess * 1000 / ctx->rate;
497 }
498
499 while (n--) {
500 ctx = limits[n].shm_zone->data;
501 lr = ctx->node;
502
503 if (lr == NULL) {
504 continue;
505 }
506
507 ngx_shmtx_lock(&ctx->shpool->mutex);
508
509 tp = ngx_timeofday();
510
511 now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
512 ms = (ngx_msec_int_t) (now - lr->last);
513
514 excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
515
516 if (excess < 0) {
517 excess = 0;
518 }
519
520 lr->last = now;
521 lr->excess = excess;
522 lr->count--;
523
524 ngx_shmtx_unlock(&ctx->shpool->mutex);
525
526 ctx->node = NULL;
527
528 if (limits[n].nodelay) {
529 continue;
530 }
531
532 delay = excess * 1000 / ctx->rate;
533
534 if (delay > max_delay) {
535 max_delay = delay;
536 *ep = excess;
537 *limit = &limits[n];
538 }
539 }
540
541 return max_delay;
420 } 542 }
421 543
422 544
423 static void 545 static void
424 ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) 546 ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
449 571
450 q = ngx_queue_last(&ctx->sh->queue); 572 q = ngx_queue_last(&ctx->sh->queue);
451 573
452 lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue); 574 lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
453 575
576 if (lr->count) {
577
578 /*
579 * There is not much sense in looking further,
580 * because we bump nodes on the lookup stage.
581 */
582
583 return;
584 }
585
454 if (n++ != 0) { 586 if (n++ != 0) {
455 587
456 ms = (ngx_msec_int_t) (now - lr->last); 588 ms = (ngx_msec_int_t) (now - lr->last);
457 ms = ngx_abs(ms); 589 ms = ngx_abs(ms);
458 590
549 } 681 }
550 682
551 /* 683 /*
552 * set by ngx_pcalloc(): 684 * set by ngx_pcalloc():
553 * 685 *
554 * conf->shm_zone = NULL; 686 * conf->limits.elts = NULL;
555 * conf->burst = 0;
556 * conf->nodelay = 0;
557 */ 687 */
558 688
559 conf->limit_log_level = NGX_CONF_UNSET_UINT; 689 conf->limit_log_level = NGX_CONF_UNSET_UINT;
560 690
561 return conf; 691 return conf;
566 ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child) 696 ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
567 { 697 {
568 ngx_http_limit_req_conf_t *prev = parent; 698 ngx_http_limit_req_conf_t *prev = parent;
569 ngx_http_limit_req_conf_t *conf = child; 699 ngx_http_limit_req_conf_t *conf = child;
570 700
571 if (conf->shm_zone == NULL) { 701 if (conf->limits.elts == NULL) {
572 conf->shm_zone = prev->shm_zone; 702 conf->limits = prev->limits;
573 conf->burst = prev->burst;
574 conf->nodelay = prev->nodelay;
575 } 703 }
576 704
577 ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level, 705 ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level,
578 NGX_LOG_ERR); 706 NGX_LOG_ERR);
579 707
586 714
587 static char * 715 static char *
588 ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 716 ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
589 { 717 {
590 u_char *p; 718 u_char *p;
591 size_t size, len; 719 size_t len;
720 ssize_t size;
592 ngx_str_t *value, name, s; 721 ngx_str_t *value, name, s;
593 ngx_int_t rate, scale; 722 ngx_int_t rate, scale;
594 ngx_uint_t i; 723 ngx_uint_t i;
595 ngx_shm_zone_t *shm_zone; 724 ngx_shm_zone_t *shm_zone;
596 ngx_http_limit_req_ctx_t *ctx; 725 ngx_http_limit_req_ctx_t *ctx;
609 738
610 name.data = value[i].data + 5; 739 name.data = value[i].data + 5;
611 740
612 p = (u_char *) ngx_strchr(name.data, ':'); 741 p = (u_char *) ngx_strchr(name.data, ':');
613 742
614 if (p) { 743 if (p == NULL) {
615 *p = '\0'; 744 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
616 745 "invalid zone size \"%V\"", &value[i]);
617 name.len = p - name.data; 746 return NGX_CONF_ERROR;
618 747 }
619 p++; 748
620 749 name.len = p - name.data;
621 s.len = value[i].data + value[i].len - p; 750
622 s.data = p; 751 s.data = p + 1;
623 752 s.len = value[i].data + value[i].len - s.data;
624 size = ngx_parse_size(&s); 753
625 if (size > 8191) { 754 size = ngx_parse_size(&s);
626 continue; 755
627 } 756 if (size == NGX_ERROR) {
628 } 757 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
629 758 "invalid zone size \"%V\"", &value[i]);
630 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 759 return NGX_CONF_ERROR;
631 "invalid zone size \"%V\"", &value[i]); 760 }
632 return NGX_CONF_ERROR; 761
762 if (size < (ssize_t) (8 * ngx_pagesize)) {
763 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
764 "zone \"%V\" is too small", &value[i]);
765 return NGX_CONF_ERROR;
766 }
767
768 continue;
633 } 769 }
634 770
635 if (ngx_strncmp(value[i].data, "rate=", 5) == 0) { 771 if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
636 772
637 len = value[i].len; 773 len = value[i].len;
679 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 815 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
680 "invalid parameter \"%V\"", &value[i]); 816 "invalid parameter \"%V\"", &value[i]);
681 return NGX_CONF_ERROR; 817 return NGX_CONF_ERROR;
682 } 818 }
683 819
684 if (name.len == 0 || size == 0) { 820 if (name.len == 0) {
685 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 821 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
686 "\"%V\" must have \"zone\" parameter", 822 "\"%V\" must have \"zone\" parameter",
687 &cmd->name); 823 &cmd->name);
688 return NGX_CONF_ERROR; 824 return NGX_CONF_ERROR;
689 } 825 }
690 826
691 if (ctx == NULL) { 827 if (ctx == NULL) {
692 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 828 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
693 "no variable is defined for limit_req_zone \"%V\"", 829 "no variable is defined for %V \"%V\"",
694 &cmd->name); 830 &cmd->name, &name);
695 return NGX_CONF_ERROR; 831 return NGX_CONF_ERROR;
696 } 832 }
697 833
698 ctx->rate = rate * 1000 / scale; 834 ctx->rate = rate * 1000 / scale;
699 835
705 841
706 if (shm_zone->data) { 842 if (shm_zone->data) {
707 ctx = shm_zone->data; 843 ctx = shm_zone->data;
708 844
709 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 845 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
710 "limit_req_zone \"%V\" is already bound to variable \"%V\"", 846 "%V \"%V\" is already bound to variable \"%V\"",
711 &value[1], &ctx->var); 847 &cmd->name, &name, &ctx->var);
712 return NGX_CONF_ERROR; 848 return NGX_CONF_ERROR;
713 } 849 }
714 850
715 shm_zone->init = ngx_http_limit_req_init_zone; 851 shm_zone->init = ngx_http_limit_req_init_zone;
716 shm_zone->data = ctx; 852 shm_zone->data = ctx;
722 static char * 858 static char *
723 ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 859 ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
724 { 860 {
725 ngx_http_limit_req_conf_t *lrcf = conf; 861 ngx_http_limit_req_conf_t *lrcf = conf;
726 862
727 ngx_int_t burst; 863 ngx_int_t burst;
728 ngx_str_t *value, s; 864 ngx_str_t *value, s;
729 ngx_uint_t i; 865 ngx_uint_t i, nodelay;
730 866 ngx_shm_zone_t *shm_zone;
731 if (lrcf->shm_zone) { 867 ngx_http_limit_req_limit_t *limit, *limits;
732 return "is duplicate";
733 }
734 868
735 value = cf->args->elts; 869 value = cf->args->elts;
736 870
871 shm_zone = NULL;
737 burst = 0; 872 burst = 0;
873 nodelay = 0;
738 874
739 for (i = 1; i < cf->args->nelts; i++) { 875 for (i = 1; i < cf->args->nelts; i++) {
740 876
741 if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { 877 if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
742 878
743 s.len = value[i].len - 5; 879 s.len = value[i].len - 5;
744 s.data = value[i].data + 5; 880 s.data = value[i].data + 5;
745 881
746 lrcf->shm_zone = ngx_shared_memory_add(cf, &s, 0, 882 shm_zone = ngx_shared_memory_add(cf, &s, 0,
747 &ngx_http_limit_req_module); 883 &ngx_http_limit_req_module);
748 if (lrcf->shm_zone == NULL) { 884 if (shm_zone == NULL) {
749 return NGX_CONF_ERROR; 885 return NGX_CONF_ERROR;
750 } 886 }
751 887
752 continue; 888 continue;
753 } 889 }
763 899
764 continue; 900 continue;
765 } 901 }
766 902
767 if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) { 903 if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) {
768 lrcf->nodelay = 1; 904 nodelay = 1;
769 continue; 905 continue;
770 } 906 }
771 907
772 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 908 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
773 "invalid parameter \"%V\"", &value[i]); 909 "invalid parameter \"%V\"", &value[i]);
774 return NGX_CONF_ERROR; 910 return NGX_CONF_ERROR;
775 } 911 }
776 912
777 if (lrcf->shm_zone == NULL) { 913 if (shm_zone == NULL) {
778 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 914 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
779 "\"%V\" must have \"zone\" parameter", 915 "\"%V\" must have \"zone\" parameter",
780 &cmd->name); 916 &cmd->name);
781 return NGX_CONF_ERROR; 917 return NGX_CONF_ERROR;
782 } 918 }
783 919
784 if (lrcf->shm_zone->data == NULL) { 920 if (shm_zone->data == NULL) {
785 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 921 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
786 "unknown limit_req_zone \"%V\"", 922 "unknown limit_req_zone \"%V\"",
787 &lrcf->shm_zone->shm.name); 923 &shm_zone->shm.name);
788 return NGX_CONF_ERROR; 924 return NGX_CONF_ERROR;
789 } 925 }
790 926
791 lrcf->burst = burst * 1000; 927 limits = lrcf->limits.elts;
928
929 if (limits == NULL) {
930 if (ngx_array_init(&lrcf->limits, cf->pool, 1,
931 sizeof(ngx_http_limit_req_limit_t))
932 != NGX_OK)
933 {
934 return NGX_CONF_ERROR;
935 }
936 }
937
938 for (i = 0; i < lrcf->limits.nelts; i++) {
939 if (shm_zone == limits[i].shm_zone) {
940 return "is duplicate";
941 }
942 }
943
944 limit = ngx_array_push(&lrcf->limits);
945
946 limit->shm_zone = shm_zone;
947 limit->burst = burst * 1000;
948 limit->nodelay = nodelay;
792 949
793 return NGX_CONF_OK; 950 return NGX_CONF_OK;
794 } 951 }
795 952
796 953