Mercurial > hg > nginx-vendor-current
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 |