Annotation of embedaddon/nginx/src/http/modules/ngx_http_limit_req_module.c, revision 1.1.1.1
1.1 misho 1:
2: /*
3: * Copyright (C) Igor Sysoev
4: * Copyright (C) Nginx, Inc.
5: */
6:
7:
8: #include <ngx_config.h>
9: #include <ngx_core.h>
10: #include <ngx_http.h>
11:
12:
13: typedef struct {
14: u_char color;
15: u_char dummy;
16: u_short len;
17: ngx_queue_t queue;
18: ngx_msec_t last;
19: /* integer value, 1 corresponds to 0.001 r/s */
20: ngx_uint_t excess;
21: ngx_uint_t count;
22: u_char data[1];
23: } ngx_http_limit_req_node_t;
24:
25:
26: typedef struct {
27: ngx_rbtree_t rbtree;
28: ngx_rbtree_node_t sentinel;
29: ngx_queue_t queue;
30: } ngx_http_limit_req_shctx_t;
31:
32:
33: typedef struct {
34: ngx_http_limit_req_shctx_t *sh;
35: ngx_slab_pool_t *shpool;
36: /* integer value, 1 corresponds to 0.001 r/s */
37: ngx_uint_t rate;
38: ngx_int_t index;
39: ngx_str_t var;
40: ngx_http_limit_req_node_t *node;
41: } ngx_http_limit_req_ctx_t;
42:
43:
44: typedef struct {
45: ngx_shm_zone_t *shm_zone;
46: /* integer value, 1 corresponds to 0.001 r/s */
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;
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;
58:
59:
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,
63: ngx_uint_t account);
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,
67: ngx_uint_t n);
68:
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,
71: void *child);
72: static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd,
73: void *conf);
74: static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd,
75: void *conf);
76: static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf);
77:
78:
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 }
85: };
86:
87:
88: static ngx_conf_num_bounds_t ngx_http_limit_req_status_bounds = {
89: ngx_conf_check_num_bounds, 400, 599
90: };
91:
92:
93: static ngx_command_t ngx_http_limit_req_commands[] = {
94:
95: { ngx_string("limit_req_zone"),
96: NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
97: ngx_http_limit_req_zone,
98: 0,
99: 0,
100: NULL },
101:
102: { ngx_string("limit_req"),
103: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
104: ngx_http_limit_req,
105: NGX_HTTP_LOC_CONF_OFFSET,
106: 0,
107: NULL },
108:
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 },
115:
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 },
122:
123: ngx_null_command
124: };
125:
126:
127: static ngx_http_module_t ngx_http_limit_req_module_ctx = {
128: NULL, /* preconfiguration */
129: ngx_http_limit_req_init, /* postconfiguration */
130:
131: NULL, /* create main configuration */
132: NULL, /* init main configuration */
133:
134: NULL, /* create server configuration */
135: NULL, /* merge server configuration */
136:
137: ngx_http_limit_req_create_conf, /* create location configuration */
138: ngx_http_limit_req_merge_conf /* merge location configuration */
139: };
140:
141:
142: ngx_module_t ngx_http_limit_req_module = {
143: NGX_MODULE_V1,
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
155: };
156:
157:
158: static ngx_int_t
159: ngx_http_limit_req_handler(ngx_http_request_t *r)
160: {
161: size_t len;
162: uint32_t hash;
163: ngx_int_t rc;
164: ngx_uint_t n, excess;
165: ngx_msec_t delay;
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;
170:
171: if (r->main->limit_req_set) {
172: return NGX_DECLINED;
173: }
174:
175: lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
176: limits = lrcf->limits.elts;
177:
178: excess = 0;
179:
180: rc = NGX_DECLINED;
181:
182: #if (NGX_SUPPRESS_WARN)
183: limit = NULL;
184: #endif
185:
186: for (n = 0; n < lrcf->limits.nelts; n++) {
187:
188: limit = &limits[n];
189:
190: ctx = limit->shm_zone->data;
191:
192: vv = ngx_http_get_indexed_variable(r, ctx->index);
193:
194: if (vv == NULL || vv->not_found) {
195: continue;
196: }
197:
198: len = vv->len;
199:
200: if (len == 0) {
201: continue;
202: }
203:
204: if (len > 65535) {
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\"",
208: &ctx->var, vv);
209: continue;
210: }
211:
212: hash = ngx_crc32_short(vv->data, len);
213:
214: ngx_shmtx_lock(&ctx->shpool->mutex);
215:
216: rc = ngx_http_limit_req_lookup(limit, hash, vv->data, len, &excess,
217: (n == lrcf->limits.nelts - 1));
218:
219: ngx_shmtx_unlock(&ctx->shpool->mutex);
220:
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);
224:
225: if (rc != NGX_AGAIN) {
226: break;
227: }
228: }
229:
230: if (rc == NGX_DECLINED) {
231: return NGX_DECLINED;
232: }
233:
234: r->main->limit_req_set = 1;
235:
236: if (rc == NGX_BUSY || rc == NGX_ERROR) {
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,
242: &limit->shm_zone->shm.name);
243: }
244:
245: while (n--) {
246: ctx = limits[n].shm_zone->data;
247:
248: if (ctx->node == NULL) {
249: continue;
250: }
251:
252: ngx_shmtx_lock(&ctx->shpool->mutex);
253:
254: ctx->node->count--;
255:
256: ngx_shmtx_unlock(&ctx->shpool->mutex);
257:
258: ctx->node = NULL;
259: }
260:
261: return lrcf->status_code;
262: }
263:
264: /* rc == NGX_AGAIN || rc == NGX_OK */
265:
266: if (rc == NGX_AGAIN) {
267: excess = 0;
268: }
269:
270: delay = ngx_http_limit_req_account(limits, n, &excess, &limit);
271:
272: if (!delay) {
273: return NGX_DECLINED;
274: }
275:
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);
279:
280: if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
281: return NGX_HTTP_INTERNAL_SERVER_ERROR;
282: }
283:
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);
287:
288: return NGX_AGAIN;
289: }
290:
291:
292: static void
293: ngx_http_limit_req_delay(ngx_http_request_t *r)
294: {
295: ngx_event_t *wev;
296:
297: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
298: "limit_req delay");
299:
300: wev = r->connection->write;
301:
302: if (!wev->timedout) {
303:
304: if (ngx_handle_write_event(wev, 0) != NGX_OK) {
305: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
306: }
307:
308: return;
309: }
310:
311: wev->timedout = 0;
312:
313: if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
314: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
315: return;
316: }
317:
318: r->read_event_handler = ngx_http_block_reading;
319: r->write_event_handler = ngx_http_core_run_phases;
320:
321: ngx_http_core_run_phases(r);
322: }
323:
324:
325: static void
326: ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp,
327: ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
328: {
329: ngx_rbtree_node_t **p;
330: ngx_http_limit_req_node_t *lrn, *lrnt;
331:
332: for ( ;; ) {
333:
334: if (node->key < temp->key) {
335:
336: p = &temp->left;
337:
338: } else if (node->key > temp->key) {
339:
340: p = &temp->right;
341:
342: } else { /* node->key == temp->key */
343:
344: lrn = (ngx_http_limit_req_node_t *) &node->color;
345: lrnt = (ngx_http_limit_req_node_t *) &temp->color;
346:
347: p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)
348: ? &temp->left : &temp->right;
349: }
350:
351: if (*p == sentinel) {
352: break;
353: }
354:
355: temp = *p;
356: }
357:
358: *p = node;
359: node->parent = temp;
360: node->left = sentinel;
361: node->right = sentinel;
362: ngx_rbt_red(node);
363: }
364:
365:
366: static ngx_int_t
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)
369: {
370: size_t size;
371: ngx_int_t rc, excess;
372: ngx_time_t *tp;
373: ngx_msec_t now;
374: ngx_msec_int_t ms;
375: ngx_rbtree_node_t *node, *sentinel;
376: ngx_http_limit_req_ctx_t *ctx;
377: ngx_http_limit_req_node_t *lr;
378:
379: tp = ngx_timeofday();
380: now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
381:
382: ctx = limit->shm_zone->data;
383:
384: node = ctx->sh->rbtree.root;
385: sentinel = ctx->sh->rbtree.sentinel;
386:
387: while (node != sentinel) {
388:
389: if (hash < node->key) {
390: node = node->left;
391: continue;
392: }
393:
394: if (hash > node->key) {
395: node = node->right;
396: continue;
397: }
398:
399: /* hash == node->key */
400:
401: lr = (ngx_http_limit_req_node_t *) &node->color;
402:
403: rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len);
404:
405: if (rc == 0) {
406: ngx_queue_remove(&lr->queue);
407: ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
408:
409: ms = (ngx_msec_int_t) (now - lr->last);
410:
411: excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
412:
413: if (excess < 0) {
414: excess = 0;
415: }
416:
417: *ep = excess;
418:
419: if ((ngx_uint_t) excess > limit->burst) {
420: return NGX_BUSY;
421: }
422:
423: if (account) {
424: lr->excess = excess;
425: lr->last = now;
426: return NGX_OK;
427: }
428:
429: lr->count++;
430:
431: ctx->node = lr;
432:
433: return NGX_AGAIN;
434: }
435:
436: node = (rc < 0) ? node->left : node->right;
437: }
438:
439: *ep = 0;
440:
441: size = offsetof(ngx_rbtree_node_t, color)
442: + offsetof(ngx_http_limit_req_node_t, data)
443: + len;
444:
445: ngx_http_limit_req_expire(ctx, 1);
446:
447: node = ngx_slab_alloc_locked(ctx->shpool, size);
448:
449: if (node == NULL) {
450: ngx_http_limit_req_expire(ctx, 0);
451:
452: node = ngx_slab_alloc_locked(ctx->shpool, size);
453: if (node == NULL) {
454: return NGX_ERROR;
455: }
456: }
457:
458: node->key = hash;
459:
460: lr = (ngx_http_limit_req_node_t *) &node->color;
461:
462: lr->len = (u_char) len;
463: lr->excess = 0;
464:
465: ngx_memcpy(lr->data, data, len);
466:
467: ngx_rbtree_insert(&ctx->sh->rbtree, node);
468:
469: ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
470:
471: if (account) {
472: lr->last = now;
473: lr->count = 0;
474: return NGX_OK;
475: }
476:
477: lr->last = 0;
478: lr->count = 1;
479:
480: ctx->node = lr;
481:
482: return NGX_AGAIN;
483: }
484:
485:
486: static ngx_msec_t
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)
489: {
490: ngx_int_t excess;
491: ngx_time_t *tp;
492: ngx_msec_t now, delay, max_delay;
493: ngx_msec_int_t ms;
494: ngx_http_limit_req_ctx_t *ctx;
495: ngx_http_limit_req_node_t *lr;
496:
497: excess = *ep;
498:
499: if (excess == 0 || (*limit)->nodelay) {
500: max_delay = 0;
501:
502: } else {
503: ctx = (*limit)->shm_zone->data;
504: max_delay = excess * 1000 / ctx->rate;
505: }
506:
507: while (n--) {
508: ctx = limits[n].shm_zone->data;
509: lr = ctx->node;
510:
511: if (lr == NULL) {
512: continue;
513: }
514:
515: ngx_shmtx_lock(&ctx->shpool->mutex);
516:
517: tp = ngx_timeofday();
518:
519: now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
520: ms = (ngx_msec_int_t) (now - lr->last);
521:
522: excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
523:
524: if (excess < 0) {
525: excess = 0;
526: }
527:
528: lr->last = now;
529: lr->excess = excess;
530: lr->count--;
531:
532: ngx_shmtx_unlock(&ctx->shpool->mutex);
533:
534: ctx->node = NULL;
535:
536: if (limits[n].nodelay) {
537: continue;
538: }
539:
540: delay = excess * 1000 / ctx->rate;
541:
542: if (delay > max_delay) {
543: max_delay = delay;
544: *ep = excess;
545: *limit = &limits[n];
546: }
547: }
548:
549: return max_delay;
550: }
551:
552:
553: static void
554: ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
555: {
556: ngx_int_t excess;
557: ngx_time_t *tp;
558: ngx_msec_t now;
559: ngx_queue_t *q;
560: ngx_msec_int_t ms;
561: ngx_rbtree_node_t *node;
562: ngx_http_limit_req_node_t *lr;
563:
564: tp = ngx_timeofday();
565:
566: now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
567:
568: /*
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
572: */
573:
574: while (n < 3) {
575:
576: if (ngx_queue_empty(&ctx->sh->queue)) {
577: return;
578: }
579:
580: q = ngx_queue_last(&ctx->sh->queue);
581:
582: lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
583:
584: if (lr->count) {
585:
586: /*
587: * There is not much sense in looking further,
588: * because we bump nodes on the lookup stage.
589: */
590:
591: return;
592: }
593:
594: if (n++ != 0) {
595:
596: ms = (ngx_msec_int_t) (now - lr->last);
597: ms = ngx_abs(ms);
598:
599: if (ms < 60000) {
600: return;
601: }
602:
603: excess = lr->excess - ctx->rate * ms / 1000;
604:
605: if (excess > 0) {
606: return;
607: }
608: }
609:
610: ngx_queue_remove(q);
611:
612: node = (ngx_rbtree_node_t *)
613: ((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
614:
615: ngx_rbtree_delete(&ctx->sh->rbtree, node);
616:
617: ngx_slab_free_locked(ctx->shpool, node);
618: }
619: }
620:
621:
622: static ngx_int_t
623: ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)
624: {
625: ngx_http_limit_req_ctx_t *octx = data;
626:
627: size_t len;
628: ngx_http_limit_req_ctx_t *ctx;
629:
630: ctx = shm_zone->data;
631:
632: if (octx) {
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);
638: return NGX_ERROR;
639: }
640:
641: ctx->sh = octx->sh;
642: ctx->shpool = octx->shpool;
643:
644: return NGX_OK;
645: }
646:
647: ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
648:
649: if (shm_zone->shm.exists) {
650: ctx->sh = ctx->shpool->data;
651:
652: return NGX_OK;
653: }
654:
655: ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t));
656: if (ctx->sh == NULL) {
657: return NGX_ERROR;
658: }
659:
660: ctx->shpool->data = ctx->sh;
661:
662: ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
663: ngx_http_limit_req_rbtree_insert_value);
664:
665: ngx_queue_init(&ctx->sh->queue);
666:
667: len = sizeof(" in limit_req zone \"\"") + shm_zone->shm.name.len;
668:
669: ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
670: if (ctx->shpool->log_ctx == NULL) {
671: return NGX_ERROR;
672: }
673:
674: ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",
675: &shm_zone->shm.name);
676:
677: return NGX_OK;
678: }
679:
680:
681: static void *
682: ngx_http_limit_req_create_conf(ngx_conf_t *cf)
683: {
684: ngx_http_limit_req_conf_t *conf;
685:
686: conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));
687: if (conf == NULL) {
688: return NULL;
689: }
690:
691: /*
692: * set by ngx_pcalloc():
693: *
694: * conf->limits.elts = NULL;
695: */
696:
697: conf->limit_log_level = NGX_CONF_UNSET_UINT;
698: conf->status_code = NGX_CONF_UNSET_UINT;
699:
700: return conf;
701: }
702:
703:
704: static char *
705: ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
706: {
707: ngx_http_limit_req_conf_t *prev = parent;
708: ngx_http_limit_req_conf_t *conf = child;
709:
710: if (conf->limits.elts == NULL) {
711: conf->limits = prev->limits;
712: }
713:
714: ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level,
715: NGX_LOG_ERR);
716:
717: conf->delay_log_level = (conf->limit_log_level == NGX_LOG_INFO) ?
718: NGX_LOG_INFO : conf->limit_log_level + 1;
719:
720: ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
721: NGX_HTTP_SERVICE_UNAVAILABLE);
722:
723: return NGX_CONF_OK;
724: }
725:
726:
727: static char *
728: ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
729: {
730: u_char *p;
731: size_t len;
732: ssize_t size;
733: ngx_str_t *value, name, s;
734: ngx_int_t rate, scale;
735: ngx_uint_t i;
736: ngx_shm_zone_t *shm_zone;
737: ngx_http_limit_req_ctx_t *ctx;
738:
739: value = cf->args->elts;
740:
741: ctx = NULL;
742: size = 0;
743: rate = 1;
744: scale = 1;
745: name.len = 0;
746:
747: for (i = 1; i < cf->args->nelts; i++) {
748:
749: if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
750:
751: name.data = value[i].data + 5;
752:
753: p = (u_char *) ngx_strchr(name.data, ':');
754:
755: if (p == NULL) {
756: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
757: "invalid zone size \"%V\"", &value[i]);
758: return NGX_CONF_ERROR;
759: }
760:
761: name.len = p - name.data;
762:
763: s.data = p + 1;
764: s.len = value[i].data + value[i].len - s.data;
765:
766: size = ngx_parse_size(&s);
767:
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;
772: }
773:
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;
778: }
779:
780: continue;
781: }
782:
783: if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
784:
785: len = value[i].len;
786: p = value[i].data + len - 3;
787:
788: if (ngx_strncmp(p, "r/s", 3) == 0) {
789: scale = 1;
790: len -= 3;
791:
792: } else if (ngx_strncmp(p, "r/m", 3) == 0) {
793: scale = 60;
794: len -= 3;
795: }
796:
797: rate = ngx_atoi(value[i].data + 5, len - 5);
798: if (rate <= 0) {
799: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
800: "invalid rate \"%V\"", &value[i]);
801: return NGX_CONF_ERROR;
802: }
803:
804: continue;
805: }
806:
807: if (value[i].data[0] == '$') {
808:
809: value[i].len--;
810: value[i].data++;
811:
812: ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));
813: if (ctx == NULL) {
814: return NGX_CONF_ERROR;
815: }
816:
817: ctx->index = ngx_http_get_variable_index(cf, &value[i]);
818: if (ctx->index == NGX_ERROR) {
819: return NGX_CONF_ERROR;
820: }
821:
822: ctx->var = value[i];
823:
824: continue;
825: }
826:
827: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
828: "invalid parameter \"%V\"", &value[i]);
829: return NGX_CONF_ERROR;
830: }
831:
832: if (name.len == 0) {
833: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
834: "\"%V\" must have \"zone\" parameter",
835: &cmd->name);
836: return NGX_CONF_ERROR;
837: }
838:
839: if (ctx == NULL) {
840: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
841: "no variable is defined for %V \"%V\"",
842: &cmd->name, &name);
843: return NGX_CONF_ERROR;
844: }
845:
846: ctx->rate = rate * 1000 / scale;
847:
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;
852: }
853:
854: if (shm_zone->data) {
855: ctx = shm_zone->data;
856:
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;
861: }
862:
863: shm_zone->init = ngx_http_limit_req_init_zone;
864: shm_zone->data = ctx;
865:
866: return NGX_CONF_OK;
867: }
868:
869:
870: static char *
871: ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
872: {
873: ngx_http_limit_req_conf_t *lrcf = conf;
874:
875: ngx_int_t burst;
876: ngx_str_t *value, s;
877: ngx_uint_t i, nodelay;
878: ngx_shm_zone_t *shm_zone;
879: ngx_http_limit_req_limit_t *limit, *limits;
880:
881: value = cf->args->elts;
882:
883: shm_zone = NULL;
884: burst = 0;
885: nodelay = 0;
886:
887: for (i = 1; i < cf->args->nelts; i++) {
888:
889: if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
890:
891: s.len = value[i].len - 5;
892: s.data = value[i].data + 5;
893:
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;
898: }
899:
900: continue;
901: }
902:
903: if (ngx_strncmp(value[i].data, "burst=", 6) == 0) {
904:
905: burst = ngx_atoi(value[i].data + 6, value[i].len - 6);
906: if (burst <= 0) {
907: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
908: "invalid burst rate \"%V\"", &value[i]);
909: return NGX_CONF_ERROR;
910: }
911:
912: continue;
913: }
914:
915: if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) {
916: nodelay = 1;
917: continue;
918: }
919:
920: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
921: "invalid parameter \"%V\"", &value[i]);
922: return NGX_CONF_ERROR;
923: }
924:
925: if (shm_zone == NULL) {
926: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
927: "\"%V\" must have \"zone\" parameter",
928: &cmd->name);
929: return NGX_CONF_ERROR;
930: }
931:
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;
937: }
938:
939: limits = lrcf->limits.elts;
940:
941: if (limits == NULL) {
942: if (ngx_array_init(&lrcf->limits, cf->pool, 1,
943: sizeof(ngx_http_limit_req_limit_t))
944: != NGX_OK)
945: {
946: return NGX_CONF_ERROR;
947: }
948: }
949:
950: for (i = 0; i < lrcf->limits.nelts; i++) {
951: if (shm_zone == limits[i].shm_zone) {
952: return "is duplicate";
953: }
954: }
955:
956: limit = ngx_array_push(&lrcf->limits);
957: if (limit == NULL) {
958: return NGX_CONF_ERROR;
959: }
960:
961: limit->shm_zone = shm_zone;
962: limit->burst = burst * 1000;
963: limit->nodelay = nodelay;
964:
965: return NGX_CONF_OK;
966: }
967:
968:
969: static ngx_int_t
970: ngx_http_limit_req_init(ngx_conf_t *cf)
971: {
972: ngx_http_handler_pt *h;
973: ngx_http_core_main_conf_t *cmcf;
974:
975: cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
976:
977: h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
978: if (h == NULL) {
979: return NGX_ERROR;
980: }
981:
982: *h = ngx_http_limit_req_handler;
983:
984: return NGX_OK;
985: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>