Mercurial > hg > nginx-ranges
comparison src/http/modules/ngx_http_limit_req_module.c @ 435:e7dbea1ee115 NGINX_0_7_25
nginx 0.7.25
*) Change: in subrequest processing.
*) Change: now POSTs without "Content-Length" header line are allowed.
*) Bugfix: now the "limit_req" and "limit_conn" directives log a
prohibition reason.
*) Bugfix: in the "delete" parameter of the "geo" directive.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Mon, 08 Dec 2008 00:00:00 +0300 |
parents | ad0a34a8efa6 |
children | 56baf312c1b5 |
comparison
equal
deleted
inserted
replaced
434:f64d9e30046c | 435:e7dbea1ee115 |
---|---|
36 ngx_uint_t nodelay;/* unsigned nodelay:1 */ | 36 ngx_uint_t nodelay;/* unsigned nodelay:1 */ |
37 } ngx_http_limit_req_conf_t; | 37 } ngx_http_limit_req_conf_t; |
38 | 38 |
39 | 39 |
40 static void ngx_http_limit_req_delay(ngx_http_request_t *r); | 40 static void ngx_http_limit_req_delay(ngx_http_request_t *r); |
41 static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lzcf, | 41 static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, |
42 ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lzp); | 42 ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lrp); |
43 static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, | 43 static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, |
44 ngx_uint_t n); | 44 ngx_uint_t n); |
45 | 45 |
46 static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf); | 46 static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf); |
47 static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, | 47 static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, |
113 ngx_uint_t excess; | 113 ngx_uint_t excess; |
114 ngx_time_t *tp; | 114 ngx_time_t *tp; |
115 ngx_rbtree_node_t *node; | 115 ngx_rbtree_node_t *node; |
116 ngx_http_variable_value_t *vv; | 116 ngx_http_variable_value_t *vv; |
117 ngx_http_limit_req_ctx_t *ctx; | 117 ngx_http_limit_req_ctx_t *ctx; |
118 ngx_http_limit_req_node_t *lz; | 118 ngx_http_limit_req_node_t *lr; |
119 ngx_http_limit_req_conf_t *lzcf; | 119 ngx_http_limit_req_conf_t *lrcf; |
120 | 120 |
121 if (r->main->limit_req_set) { | 121 if (r->main->limit_req_set) { |
122 return NGX_DECLINED; | 122 return NGX_DECLINED; |
123 } | 123 } |
124 | 124 |
125 lzcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); | 125 lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); |
126 | 126 |
127 if (lzcf->shm_zone == NULL) { | 127 if (lrcf->shm_zone == NULL) { |
128 return NGX_DECLINED; | 128 return NGX_DECLINED; |
129 } | 129 } |
130 | 130 |
131 ctx = lzcf->shm_zone->data; | 131 ctx = lrcf->shm_zone->data; |
132 | 132 |
133 vv = ngx_http_get_indexed_variable(r, ctx->index); | 133 vv = ngx_http_get_indexed_variable(r, ctx->index); |
134 | 134 |
135 if (vv == NULL || vv->not_found) { | 135 if (vv == NULL || vv->not_found) { |
136 return NGX_DECLINED; | 136 return NGX_DECLINED; |
156 | 156 |
157 ngx_shmtx_lock(&ctx->shpool->mutex); | 157 ngx_shmtx_lock(&ctx->shpool->mutex); |
158 | 158 |
159 ngx_http_limit_req_expire(ctx, 1); | 159 ngx_http_limit_req_expire(ctx, 1); |
160 | 160 |
161 rc = ngx_http_limit_req_lookup(lzcf, hash, vv->data, len, &lz); | 161 rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &lr); |
162 | 162 |
163 if (lz) { | 163 if (lr) { |
164 ngx_queue_remove(&lz->queue); | 164 ngx_queue_remove(&lr->queue); |
165 | 165 |
166 ngx_queue_insert_head(ctx->queue, &lz->queue); | 166 ngx_queue_insert_head(ctx->queue, &lr->queue); |
167 | 167 |
168 excess = lz->excess; | 168 excess = lr->excess; |
169 | 169 |
170 } else { | 170 } else { |
171 excess = 0; | 171 excess = 0; |
172 } | 172 } |
173 | 173 |
176 | 176 |
177 if (rc == NGX_BUSY) { | 177 if (rc == NGX_BUSY) { |
178 ngx_shmtx_unlock(&ctx->shpool->mutex); | 178 ngx_shmtx_unlock(&ctx->shpool->mutex); |
179 | 179 |
180 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | 180 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
181 "limiting requests, excess: %ui.%03ui", | 181 "limiting requests, excess: %ui.%03ui by zone \"%V\"", |
182 excess / 1000, excess % 1000); | 182 excess / 1000, excess % 1000, &lrcf->shm_zone->name); |
183 | 183 |
184 return NGX_HTTP_SERVICE_UNAVAILABLE; | 184 return NGX_HTTP_SERVICE_UNAVAILABLE; |
185 } | 185 } |
186 | 186 |
187 if (rc == NGX_AGAIN) { | 187 if (rc == NGX_AGAIN) { |
188 ngx_shmtx_unlock(&ctx->shpool->mutex); | 188 ngx_shmtx_unlock(&ctx->shpool->mutex); |
189 | 189 |
190 if (lzcf->nodelay) { | 190 if (lrcf->nodelay) { |
191 return NGX_DECLINED; | 191 return NGX_DECLINED; |
192 } | 192 } |
193 | 193 |
194 ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, | 194 ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, |
195 "delaying request, excess: %ui.%03ui", | 195 "delaying request, excess: %ui.%03ui, by zone \"%V\"", |
196 excess / 1000, excess % 1000); | 196 excess / 1000, excess % 1000, &lrcf->shm_zone->name); |
197 | 197 |
198 if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { | 198 if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { |
199 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 199 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
200 } | 200 } |
201 | 201 |
222 ngx_http_limit_req_expire(ctx, 0); | 222 ngx_http_limit_req_expire(ctx, 0); |
223 | 223 |
224 node = ngx_slab_alloc_locked(ctx->shpool, n); | 224 node = ngx_slab_alloc_locked(ctx->shpool, n); |
225 if (node == NULL) { | 225 if (node == NULL) { |
226 ngx_shmtx_unlock(&ctx->shpool->mutex); | 226 ngx_shmtx_unlock(&ctx->shpool->mutex); |
227 | |
228 ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, | |
229 "could not allocate memory in zone \"%V\"", | |
230 &lrcf->shm_zone->name); | |
231 | |
227 return NGX_HTTP_SERVICE_UNAVAILABLE; | 232 return NGX_HTTP_SERVICE_UNAVAILABLE; |
228 } | 233 } |
229 } | 234 } |
230 | 235 |
231 lz = (ngx_http_limit_req_node_t *) &node->color; | 236 lr = (ngx_http_limit_req_node_t *) &node->color; |
232 | 237 |
233 node->key = hash; | 238 node->key = hash; |
234 lz->len = (u_char) len; | 239 lr->len = (u_char) len; |
235 | 240 |
236 tp = ngx_timeofday(); | 241 tp = ngx_timeofday(); |
237 lz->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); | 242 lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); |
238 | 243 |
239 lz->excess = 0; | 244 lr->excess = 0; |
240 ngx_memcpy(lz->data, vv->data, len); | 245 ngx_memcpy(lr->data, vv->data, len); |
241 | 246 |
242 ngx_rbtree_insert(ctx->rbtree, node); | 247 ngx_rbtree_insert(ctx->rbtree, node); |
243 | 248 |
244 ngx_queue_insert_head(ctx->queue, &lz->queue); | 249 ngx_queue_insert_head(ctx->queue, &lr->queue); |
245 | 250 |
246 done: | 251 done: |
247 | 252 |
248 ngx_shmtx_unlock(&ctx->shpool->mutex); | 253 ngx_shmtx_unlock(&ctx->shpool->mutex); |
249 | 254 |
272 static void | 277 static void |
273 ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp, | 278 ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp, |
274 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) | 279 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) |
275 { | 280 { |
276 ngx_rbtree_node_t **p; | 281 ngx_rbtree_node_t **p; |
277 ngx_http_limit_req_node_t *lzn, *lznt; | 282 ngx_http_limit_req_node_t *lrn, *lrnt; |
278 | 283 |
279 for ( ;; ) { | 284 for ( ;; ) { |
280 | 285 |
281 if (node->key < temp->key) { | 286 if (node->key < temp->key) { |
282 | 287 |
286 | 291 |
287 p = &temp->right; | 292 p = &temp->right; |
288 | 293 |
289 } else { /* node->key == temp->key */ | 294 } else { /* node->key == temp->key */ |
290 | 295 |
291 lzn = (ngx_http_limit_req_node_t *) &node->color; | 296 lrn = (ngx_http_limit_req_node_t *) &node->color; |
292 lznt = (ngx_http_limit_req_node_t *) &temp->color; | 297 lrnt = (ngx_http_limit_req_node_t *) &temp->color; |
293 | 298 |
294 p = (ngx_memn2cmp(lzn->data, lznt->data, lzn->len, lznt->len) < 0) | 299 p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0) |
295 ? &temp->left : &temp->right; | 300 ? &temp->left : &temp->right; |
296 } | 301 } |
297 | 302 |
298 if (*p == sentinel) { | 303 if (*p == sentinel) { |
299 break; | 304 break; |
309 ngx_rbt_red(node); | 314 ngx_rbt_red(node); |
310 } | 315 } |
311 | 316 |
312 | 317 |
313 static ngx_int_t | 318 static ngx_int_t |
314 ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lzcf, ngx_uint_t hash, | 319 ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash, |
315 u_char *data, size_t len, ngx_http_limit_req_node_t **lzp) | 320 u_char *data, size_t len, ngx_http_limit_req_node_t **lrp) |
316 { | 321 { |
317 ngx_int_t rc, excess; | 322 ngx_int_t rc, excess; |
318 ngx_time_t *tp; | 323 ngx_time_t *tp; |
319 ngx_msec_t now; | 324 ngx_msec_t now; |
320 ngx_msec_int_t ms; | 325 ngx_msec_int_t ms; |
321 ngx_rbtree_node_t *node, *sentinel; | 326 ngx_rbtree_node_t *node, *sentinel; |
322 ngx_http_limit_req_ctx_t *ctx; | 327 ngx_http_limit_req_ctx_t *ctx; |
323 ngx_http_limit_req_node_t *lz; | 328 ngx_http_limit_req_node_t *lr; |
324 | 329 |
325 ctx = lzcf->shm_zone->data; | 330 ctx = lrcf->shm_zone->data; |
326 | 331 |
327 node = ctx->rbtree->root; | 332 node = ctx->rbtree->root; |
328 sentinel = ctx->rbtree->sentinel; | 333 sentinel = ctx->rbtree->sentinel; |
329 | 334 |
330 while (node != sentinel) { | 335 while (node != sentinel) { |
340 } | 345 } |
341 | 346 |
342 /* hash == node->key */ | 347 /* hash == node->key */ |
343 | 348 |
344 do { | 349 do { |
345 lz = (ngx_http_limit_req_node_t *) &node->color; | 350 lr = (ngx_http_limit_req_node_t *) &node->color; |
346 | 351 |
347 rc = ngx_memn2cmp(data, lz->data, len, (size_t) lz->len); | 352 rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len); |
348 | 353 |
349 if (rc == 0) { | 354 if (rc == 0) { |
350 | 355 |
351 tp = ngx_timeofday(); | 356 tp = ngx_timeofday(); |
352 | 357 |
353 now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); | 358 now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); |
354 ms = (ngx_msec_int_t) (now - lz->last); | 359 ms = (ngx_msec_int_t) (now - lr->last); |
355 | 360 |
356 excess = lz->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; | 361 excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; |
357 | 362 |
358 if (excess < 0) { | 363 if (excess < 0) { |
359 excess = 0; | 364 excess = 0; |
360 } | 365 } |
361 | 366 |
362 lz->excess = excess; | 367 lr->excess = excess; |
363 lz->last = now; | 368 lr->last = now; |
364 | 369 |
365 *lzp = lz; | 370 *lrp = lr; |
366 | 371 |
367 if ((ngx_uint_t) excess > lzcf->burst) { | 372 if ((ngx_uint_t) excess > lrcf->burst) { |
368 return NGX_BUSY; | 373 return NGX_BUSY; |
369 } | 374 } |
370 | 375 |
371 if (excess) { | 376 if (excess) { |
372 return NGX_AGAIN; | 377 return NGX_AGAIN; |
380 } while (node != sentinel && hash == node->key); | 385 } while (node != sentinel && hash == node->key); |
381 | 386 |
382 break; | 387 break; |
383 } | 388 } |
384 | 389 |
385 *lzp = NULL; | 390 *lrp = NULL; |
386 | 391 |
387 return NGX_DECLINED; | 392 return NGX_DECLINED; |
388 } | 393 } |
389 | 394 |
390 | 395 |
395 ngx_time_t *tp; | 400 ngx_time_t *tp; |
396 ngx_msec_t now; | 401 ngx_msec_t now; |
397 ngx_queue_t *q; | 402 ngx_queue_t *q; |
398 ngx_msec_int_t ms; | 403 ngx_msec_int_t ms; |
399 ngx_rbtree_node_t *node; | 404 ngx_rbtree_node_t *node; |
400 ngx_http_limit_req_node_t *lz; | 405 ngx_http_limit_req_node_t *lr; |
401 | 406 |
402 tp = ngx_timeofday(); | 407 tp = ngx_timeofday(); |
403 | 408 |
404 now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); | 409 now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); |
405 | 410 |
415 return; | 420 return; |
416 } | 421 } |
417 | 422 |
418 q = ngx_queue_last(ctx->queue); | 423 q = ngx_queue_last(ctx->queue); |
419 | 424 |
420 lz = ngx_queue_data(q, ngx_http_limit_req_node_t, queue); | 425 lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue); |
421 | 426 |
422 if (n++ != 0) { | 427 if (n++ != 0) { |
423 | 428 |
424 ms = (ngx_msec_int_t) (now - lz->last); | 429 ms = (ngx_msec_int_t) (now - lr->last); |
425 ms = ngx_abs(ms); | 430 ms = ngx_abs(ms); |
426 | 431 |
427 if (ms < 60000) { | 432 if (ms < 60000) { |
428 return; | 433 return; |
429 } | 434 } |
430 | 435 |
431 excess = lz->excess - ctx->rate * ms / 1000; | 436 excess = lr->excess - ctx->rate * ms / 1000; |
432 | 437 |
433 if (excess > 0) { | 438 if (excess > 0) { |
434 return; | 439 return; |
435 } | 440 } |
436 } | 441 } |
437 | 442 |
438 ngx_queue_remove(q); | 443 ngx_queue_remove(q); |
439 | 444 |
440 node = (ngx_rbtree_node_t *) | 445 node = (ngx_rbtree_node_t *) |
441 ((u_char *) lz - offsetof(ngx_rbtree_node_t, color)); | 446 ((u_char *) lr - offsetof(ngx_rbtree_node_t, color)); |
442 | 447 |
443 ngx_rbtree_delete(ctx->rbtree, node); | 448 ngx_rbtree_delete(ctx->rbtree, node); |
444 | 449 |
445 ngx_slab_free_locked(ctx->shpool, node); | 450 ngx_slab_free_locked(ctx->shpool, node); |
446 } | 451 } |
511 | 516 |
512 /* | 517 /* |
513 * set by ngx_pcalloc(): | 518 * set by ngx_pcalloc(): |
514 * | 519 * |
515 * conf->shm_zone = NULL; | 520 * conf->shm_zone = NULL; |
516 * conf->burst = 0.0; | 521 * conf->burst = 0; |
517 * conf->nodelay = 0; | 522 * conf->nodelay = 0; |
518 */ | 523 */ |
519 | 524 |
520 return conf; | 525 return conf; |
521 } | 526 } |
669 | 674 |
670 | 675 |
671 static char * | 676 static char * |
672 ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | 677 ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
673 { | 678 { |
674 ngx_http_limit_req_conf_t *lzcf = conf; | 679 ngx_http_limit_req_conf_t *lrcf = conf; |
675 | 680 |
676 ngx_int_t burst; | 681 ngx_int_t burst; |
677 ngx_str_t *value, s; | 682 ngx_str_t *value, s; |
678 ngx_uint_t i; | 683 ngx_uint_t i; |
679 | 684 |
680 if (lzcf->shm_zone) { | 685 if (lrcf->shm_zone) { |
681 return "is duplicate"; | 686 return "is duplicate"; |
682 } | 687 } |
683 | 688 |
684 value = cf->args->elts; | 689 value = cf->args->elts; |
685 | 690 |
690 if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { | 695 if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { |
691 | 696 |
692 s.len = value[i].len - 5; | 697 s.len = value[i].len - 5; |
693 s.data = value[i].data + 5; | 698 s.data = value[i].data + 5; |
694 | 699 |
695 lzcf->shm_zone = ngx_shared_memory_add(cf, &s, 0, | 700 lrcf->shm_zone = ngx_shared_memory_add(cf, &s, 0, |
696 &ngx_http_limit_req_module); | 701 &ngx_http_limit_req_module); |
697 if (lzcf->shm_zone == NULL) { | 702 if (lrcf->shm_zone == NULL) { |
698 return NGX_CONF_ERROR; | 703 return NGX_CONF_ERROR; |
699 } | 704 } |
700 | 705 |
701 continue; | 706 continue; |
702 } | 707 } |
712 | 717 |
713 continue; | 718 continue; |
714 } | 719 } |
715 | 720 |
716 if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) { | 721 if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) { |
717 lzcf->nodelay = 1; | 722 lrcf->nodelay = 1; |
718 continue; | 723 continue; |
719 } | 724 } |
720 | 725 |
721 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 726 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
722 "invalid parameter \"%V\"", &value[i]); | 727 "invalid parameter \"%V\"", &value[i]); |
723 return NGX_CONF_ERROR; | 728 return NGX_CONF_ERROR; |
724 } | 729 } |
725 | 730 |
726 if (lzcf->shm_zone == NULL) { | 731 if (lrcf->shm_zone == NULL) { |
727 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 732 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
728 "\"%V\" must have \"zone\" parameter", | 733 "\"%V\" must have \"zone\" parameter", |
729 &cmd->name); | 734 &cmd->name); |
730 return NGX_CONF_ERROR; | 735 return NGX_CONF_ERROR; |
731 } | 736 } |
732 | 737 |
733 if (lzcf->shm_zone->data == NULL) { | 738 if (lrcf->shm_zone->data == NULL) { |
734 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 739 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
735 "unknown limit_req_zone \"%V\"", | 740 "unknown limit_req_zone \"%V\"", |
736 &lzcf->shm_zone->name); | 741 &lrcf->shm_zone->name); |
737 return NGX_CONF_ERROR; | 742 return NGX_CONF_ERROR; |
738 } | 743 } |
739 | 744 |
740 lzcf->burst = burst * 1000; | 745 lrcf->burst = burst * 1000; |
741 | 746 |
742 return NGX_CONF_OK; | 747 return NGX_CONF_OK; |
743 } | 748 } |
744 | 749 |
745 | 750 |