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