Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 typedef struct {
0014     u_char                       color;
0015     u_char                       dummy;
0016     u_short                      len;
0017     ngx_queue_t                  queue;
0018     ngx_msec_t                   last;
0019     /* integer value, 1 corresponds to 0.001 r/s */
0020     ngx_uint_t                   excess;
0021     ngx_uint_t                   count;
0022     u_char                       data[1];
0023 } ngx_http_limit_req_node_t;
0024 
0025 
0026 typedef struct {
0027     ngx_rbtree_t                  rbtree;
0028     ngx_rbtree_node_t             sentinel;
0029     ngx_queue_t                   queue;
0030 } ngx_http_limit_req_shctx_t;
0031 
0032 
0033 typedef struct {
0034     ngx_http_limit_req_shctx_t  *sh;
0035     ngx_slab_pool_t             *shpool;
0036     /* integer value, 1 corresponds to 0.001 r/s */
0037     ngx_uint_t                   rate;
0038     ngx_http_complex_value_t     key;
0039     ngx_http_limit_req_node_t   *node;
0040 } ngx_http_limit_req_ctx_t;
0041 
0042 
0043 typedef struct {
0044     ngx_shm_zone_t              *shm_zone;
0045     /* integer value, 1 corresponds to 0.001 r/s */
0046     ngx_uint_t                   burst;
0047     ngx_uint_t                   nodelay; /* unsigned  nodelay:1 */
0048 } ngx_http_limit_req_limit_t;
0049 
0050 
0051 typedef struct {
0052     ngx_array_t                  limits;
0053     ngx_uint_t                   limit_log_level;
0054     ngx_uint_t                   delay_log_level;
0055     ngx_uint_t                   status_code;
0056 } ngx_http_limit_req_conf_t;
0057 
0058 
0059 static void ngx_http_limit_req_delay(ngx_http_request_t *r);
0060 static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit,
0061     ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account);
0062 static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits,
0063     ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit);
0064 static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
0065     ngx_uint_t n);
0066 
0067 static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf);
0068 static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent,
0069     void *child);
0070 static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd,
0071     void *conf);
0072 static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd,
0073     void *conf);
0074 static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf);
0075 
0076 
0077 static ngx_conf_enum_t  ngx_http_limit_req_log_levels[] = {
0078     { ngx_string("info"), NGX_LOG_INFO },
0079     { ngx_string("notice"), NGX_LOG_NOTICE },
0080     { ngx_string("warn"), NGX_LOG_WARN },
0081     { ngx_string("error"), NGX_LOG_ERR },
0082     { ngx_null_string, 0 }
0083 };
0084 
0085 
0086 static ngx_conf_num_bounds_t  ngx_http_limit_req_status_bounds = {
0087     ngx_conf_check_num_bounds, 400, 599
0088 };
0089 
0090 
0091 static ngx_command_t  ngx_http_limit_req_commands[] = {
0092 
0093     { ngx_string("limit_req_zone"),
0094       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
0095       ngx_http_limit_req_zone,
0096       0,
0097       0,
0098       NULL },
0099 
0100     { ngx_string("limit_req"),
0101       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
0102       ngx_http_limit_req,
0103       NGX_HTTP_LOC_CONF_OFFSET,
0104       0,
0105       NULL },
0106 
0107     { ngx_string("limit_req_log_level"),
0108       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0109       ngx_conf_set_enum_slot,
0110       NGX_HTTP_LOC_CONF_OFFSET,
0111       offsetof(ngx_http_limit_req_conf_t, limit_log_level),
0112       &ngx_http_limit_req_log_levels },
0113 
0114     { ngx_string("limit_req_status"),
0115       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0116       ngx_conf_set_num_slot,
0117       NGX_HTTP_LOC_CONF_OFFSET,
0118       offsetof(ngx_http_limit_req_conf_t, status_code),
0119       &ngx_http_limit_req_status_bounds },
0120 
0121       ngx_null_command
0122 };
0123 
0124 
0125 static ngx_http_module_t  ngx_http_limit_req_module_ctx = {
0126     NULL,                                  /* preconfiguration */
0127     ngx_http_limit_req_init,               /* postconfiguration */
0128 
0129     NULL,                                  /* create main configuration */
0130     NULL,                                  /* init main configuration */
0131 
0132     NULL,                                  /* create server configuration */
0133     NULL,                                  /* merge server configuration */
0134 
0135     ngx_http_limit_req_create_conf,        /* create location configuration */
0136     ngx_http_limit_req_merge_conf          /* merge location configuration */
0137 };
0138 
0139 
0140 ngx_module_t  ngx_http_limit_req_module = {
0141     NGX_MODULE_V1,
0142     &ngx_http_limit_req_module_ctx,        /* module context */
0143     ngx_http_limit_req_commands,           /* module directives */
0144     NGX_HTTP_MODULE,                       /* module type */
0145     NULL,                                  /* init master */
0146     NULL,                                  /* init module */
0147     NULL,                                  /* init process */
0148     NULL,                                  /* init thread */
0149     NULL,                                  /* exit thread */
0150     NULL,                                  /* exit process */
0151     NULL,                                  /* exit master */
0152     NGX_MODULE_V1_PADDING
0153 };
0154 
0155 
0156 static ngx_int_t
0157 ngx_http_limit_req_handler(ngx_http_request_t *r)
0158 {
0159     uint32_t                     hash;
0160     ngx_str_t                    key;
0161     ngx_int_t                    rc;
0162     ngx_uint_t                   n, excess;
0163     ngx_msec_t                   delay;
0164     ngx_http_limit_req_ctx_t    *ctx;
0165     ngx_http_limit_req_conf_t   *lrcf;
0166     ngx_http_limit_req_limit_t  *limit, *limits;
0167 
0168     if (r->main->limit_req_set) {
0169         return NGX_DECLINED;
0170     }
0171 
0172     lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
0173     limits = lrcf->limits.elts;
0174 
0175     excess = 0;
0176 
0177     rc = NGX_DECLINED;
0178 
0179 #if (NGX_SUPPRESS_WARN)
0180     limit = NULL;
0181 #endif
0182 
0183     for (n = 0; n < lrcf->limits.nelts; n++) {
0184 
0185         limit = &limits[n];
0186 
0187         ctx = limit->shm_zone->data;
0188 
0189         if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
0190             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0191         }
0192 
0193         if (key.len == 0) {
0194             continue;
0195         }
0196 
0197         if (key.len > 65535) {
0198             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0199                           "the value of the \"%V\" key "
0200                           "is more than 65535 bytes: \"%V\"",
0201                           &ctx->key.value, &key);
0202             continue;
0203         }
0204 
0205         hash = ngx_crc32_short(key.data, key.len);
0206 
0207         ngx_shmtx_lock(&ctx->shpool->mutex);
0208 
0209         rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess,
0210                                        (n == lrcf->limits.nelts - 1));
0211 
0212         ngx_shmtx_unlock(&ctx->shpool->mutex);
0213 
0214         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0215                        "limit_req[%ui]: %i %ui.%03ui",
0216                        n, rc, excess / 1000, excess % 1000);
0217 
0218         if (rc != NGX_AGAIN) {
0219             break;
0220         }
0221     }
0222 
0223     if (rc == NGX_DECLINED) {
0224         return NGX_DECLINED;
0225     }
0226 
0227     r->main->limit_req_set = 1;
0228 
0229     if (rc == NGX_BUSY || rc == NGX_ERROR) {
0230 
0231         if (rc == NGX_BUSY) {
0232             ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
0233                           "limiting requests, excess: %ui.%03ui by zone \"%V\"",
0234                           excess / 1000, excess % 1000,
0235                           &limit->shm_zone->shm.name);
0236         }
0237 
0238         while (n--) {
0239             ctx = limits[n].shm_zone->data;
0240 
0241             if (ctx->node == NULL) {
0242                 continue;
0243             }
0244 
0245             ngx_shmtx_lock(&ctx->shpool->mutex);
0246 
0247             ctx->node->count--;
0248 
0249             ngx_shmtx_unlock(&ctx->shpool->mutex);
0250 
0251             ctx->node = NULL;
0252         }
0253 
0254         return lrcf->status_code;
0255     }
0256 
0257     /* rc == NGX_AGAIN || rc == NGX_OK */
0258 
0259     if (rc == NGX_AGAIN) {
0260         excess = 0;
0261     }
0262 
0263     delay = ngx_http_limit_req_account(limits, n, &excess, &limit);
0264 
0265     if (!delay) {
0266         return NGX_DECLINED;
0267     }
0268 
0269     ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
0270                   "delaying request, excess: %ui.%03ui, by zone \"%V\"",
0271                   excess / 1000, excess % 1000, &limit->shm_zone->shm.name);
0272 
0273     if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
0274         return NGX_HTTP_INTERNAL_SERVER_ERROR;
0275     }
0276 
0277     r->read_event_handler = ngx_http_test_reading;
0278     r->write_event_handler = ngx_http_limit_req_delay;
0279 
0280     r->connection->write->delayed = 1;
0281     ngx_add_timer(r->connection->write, delay);
0282 
0283     return NGX_AGAIN;
0284 }
0285 
0286 
0287 static void
0288 ngx_http_limit_req_delay(ngx_http_request_t *r)
0289 {
0290     ngx_event_t  *wev;
0291 
0292     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0293                    "limit_req delay");
0294 
0295     wev = r->connection->write;
0296 
0297     if (wev->delayed) {
0298 
0299         if (ngx_handle_write_event(wev, 0) != NGX_OK) {
0300             ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
0301         }
0302 
0303         return;
0304     }
0305 
0306     if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
0307         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
0308         return;
0309     }
0310 
0311     r->read_event_handler = ngx_http_block_reading;
0312     r->write_event_handler = ngx_http_core_run_phases;
0313 
0314     ngx_http_core_run_phases(r);
0315 }
0316 
0317 
0318 static void
0319 ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp,
0320     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
0321 {
0322     ngx_rbtree_node_t          **p;
0323     ngx_http_limit_req_node_t   *lrn, *lrnt;
0324 
0325     for ( ;; ) {
0326 
0327         if (node->key < temp->key) {
0328 
0329             p = &temp->left;
0330 
0331         } else if (node->key > temp->key) {
0332 
0333             p = &temp->right;
0334 
0335         } else { /* node->key == temp->key */
0336 
0337             lrn = (ngx_http_limit_req_node_t *) &node->color;
0338             lrnt = (ngx_http_limit_req_node_t *) &temp->color;
0339 
0340             p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)
0341                 ? &temp->left : &temp->right;
0342         }
0343 
0344         if (*p == sentinel) {
0345             break;
0346         }
0347 
0348         temp = *p;
0349     }
0350 
0351     *p = node;
0352     node->parent = temp;
0353     node->left = sentinel;
0354     node->right = sentinel;
0355     ngx_rbt_red(node);
0356 }
0357 
0358 
0359 static ngx_int_t
0360 ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash,
0361     ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account)
0362 {
0363     size_t                      size;
0364     ngx_int_t                   rc, excess;
0365     ngx_msec_t                  now;
0366     ngx_msec_int_t              ms;
0367     ngx_rbtree_node_t          *node, *sentinel;
0368     ngx_http_limit_req_ctx_t   *ctx;
0369     ngx_http_limit_req_node_t  *lr;
0370 
0371     now = ngx_current_msec;
0372 
0373     ctx = limit->shm_zone->data;
0374 
0375     node = ctx->sh->rbtree.root;
0376     sentinel = ctx->sh->rbtree.sentinel;
0377 
0378     while (node != sentinel) {
0379 
0380         if (hash < node->key) {
0381             node = node->left;
0382             continue;
0383         }
0384 
0385         if (hash > node->key) {
0386             node = node->right;
0387             continue;
0388         }
0389 
0390         /* hash == node->key */
0391 
0392         lr = (ngx_http_limit_req_node_t *) &node->color;
0393 
0394         rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len);
0395 
0396         if (rc == 0) {
0397             ngx_queue_remove(&lr->queue);
0398             ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
0399 
0400             ms = (ngx_msec_int_t) (now - lr->last);
0401 
0402             excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
0403 
0404             if (excess < 0) {
0405                 excess = 0;
0406             }
0407 
0408             *ep = excess;
0409 
0410             if ((ngx_uint_t) excess > limit->burst) {
0411                 return NGX_BUSY;
0412             }
0413 
0414             if (account) {
0415                 lr->excess = excess;
0416                 lr->last = now;
0417                 return NGX_OK;
0418             }
0419 
0420             lr->count++;
0421 
0422             ctx->node = lr;
0423 
0424             return NGX_AGAIN;
0425         }
0426 
0427         node = (rc < 0) ? node->left : node->right;
0428     }
0429 
0430     *ep = 0;
0431 
0432     size = offsetof(ngx_rbtree_node_t, color)
0433            + offsetof(ngx_http_limit_req_node_t, data)
0434            + key->len;
0435 
0436     ngx_http_limit_req_expire(ctx, 1);
0437 
0438     node = ngx_slab_alloc_locked(ctx->shpool, size);
0439 
0440     if (node == NULL) {
0441         ngx_http_limit_req_expire(ctx, 0);
0442 
0443         node = ngx_slab_alloc_locked(ctx->shpool, size);
0444         if (node == NULL) {
0445             ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
0446                           "could not allocate node%s", ctx->shpool->log_ctx);
0447             return NGX_ERROR;
0448         }
0449     }
0450 
0451     node->key = hash;
0452 
0453     lr = (ngx_http_limit_req_node_t *) &node->color;
0454 
0455     lr->len = (u_short) key->len;
0456     lr->excess = 0;
0457 
0458     ngx_memcpy(lr->data, key->data, key->len);
0459 
0460     ngx_rbtree_insert(&ctx->sh->rbtree, node);
0461 
0462     ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
0463 
0464     if (account) {
0465         lr->last = now;
0466         lr->count = 0;
0467         return NGX_OK;
0468     }
0469 
0470     lr->last = 0;
0471     lr->count = 1;
0472 
0473     ctx->node = lr;
0474 
0475     return NGX_AGAIN;
0476 }
0477 
0478 
0479 static ngx_msec_t
0480 ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n,
0481     ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit)
0482 {
0483     ngx_int_t                   excess;
0484     ngx_msec_t                  now, delay, max_delay;
0485     ngx_msec_int_t              ms;
0486     ngx_http_limit_req_ctx_t   *ctx;
0487     ngx_http_limit_req_node_t  *lr;
0488 
0489     excess = *ep;
0490 
0491     if (excess == 0 || (*limit)->nodelay) {
0492         max_delay = 0;
0493 
0494     } else {
0495         ctx = (*limit)->shm_zone->data;
0496         max_delay = excess * 1000 / ctx->rate;
0497     }
0498 
0499     while (n--) {
0500         ctx = limits[n].shm_zone->data;
0501         lr = ctx->node;
0502 
0503         if (lr == NULL) {
0504             continue;
0505         }
0506 
0507         ngx_shmtx_lock(&ctx->shpool->mutex);
0508 
0509         now = ngx_current_msec;
0510         ms = (ngx_msec_int_t) (now - lr->last);
0511 
0512         excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
0513 
0514         if (excess < 0) {
0515             excess = 0;
0516         }
0517 
0518         lr->last = now;
0519         lr->excess = excess;
0520         lr->count--;
0521 
0522         ngx_shmtx_unlock(&ctx->shpool->mutex);
0523 
0524         ctx->node = NULL;
0525 
0526         if (limits[n].nodelay) {
0527             continue;
0528         }
0529 
0530         delay = excess * 1000 / ctx->rate;
0531 
0532         if (delay > max_delay) {
0533             max_delay = delay;
0534             *ep = excess;
0535             *limit = &limits[n];
0536         }
0537     }
0538 
0539     return max_delay;
0540 }
0541 
0542 
0543 static void
0544 ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
0545 {
0546     ngx_int_t                   excess;
0547     ngx_msec_t                  now;
0548     ngx_queue_t                *q;
0549     ngx_msec_int_t              ms;
0550     ngx_rbtree_node_t          *node;
0551     ngx_http_limit_req_node_t  *lr;
0552 
0553     now = ngx_current_msec;
0554 
0555     /*
0556      * n == 1 deletes one or two zero rate entries
0557      * n == 0 deletes oldest entry by force
0558      *        and one or two zero rate entries
0559      */
0560 
0561     while (n < 3) {
0562 
0563         if (ngx_queue_empty(&ctx->sh->queue)) {
0564             return;
0565         }
0566 
0567         q = ngx_queue_last(&ctx->sh->queue);
0568 
0569         lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
0570 
0571         if (lr->count) {
0572 
0573             /*
0574              * There is not much sense in looking further,
0575              * because we bump nodes on the lookup stage.
0576              */
0577 
0578             return;
0579         }
0580 
0581         if (n++ != 0) {
0582 
0583             ms = (ngx_msec_int_t) (now - lr->last);
0584             ms = ngx_abs(ms);
0585 
0586             if (ms < 60000) {
0587                 return;
0588             }
0589 
0590             excess = lr->excess - ctx->rate * ms / 1000;
0591 
0592             if (excess > 0) {
0593                 return;
0594             }
0595         }
0596 
0597         ngx_queue_remove(q);
0598 
0599         node = (ngx_rbtree_node_t *)
0600                    ((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
0601 
0602         ngx_rbtree_delete(&ctx->sh->rbtree, node);
0603 
0604         ngx_slab_free_locked(ctx->shpool, node);
0605     }
0606 }
0607 
0608 
0609 static ngx_int_t
0610 ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)
0611 {
0612     ngx_http_limit_req_ctx_t  *octx = data;
0613 
0614     size_t                     len;
0615     ngx_http_limit_req_ctx_t  *ctx;
0616 
0617     ctx = shm_zone->data;
0618 
0619     if (octx) {
0620         if (ctx->key.value.len != octx->key.value.len
0621             || ngx_strncmp(ctx->key.value.data, octx->key.value.data,
0622                            ctx->key.value.len)
0623                != 0)
0624         {
0625             ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
0626                           "limit_req \"%V\" uses the \"%V\" key "
0627                           "while previously it used the \"%V\" key",
0628                           &shm_zone->shm.name, &ctx->key.value,
0629                           &octx->key.value);
0630             return NGX_ERROR;
0631         }
0632 
0633         ctx->sh = octx->sh;
0634         ctx->shpool = octx->shpool;
0635 
0636         return NGX_OK;
0637     }
0638 
0639     ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
0640 
0641     if (shm_zone->shm.exists) {
0642         ctx->sh = ctx->shpool->data;
0643 
0644         return NGX_OK;
0645     }
0646 
0647     ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t));
0648     if (ctx->sh == NULL) {
0649         return NGX_ERROR;
0650     }
0651 
0652     ctx->shpool->data = ctx->sh;
0653 
0654     ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
0655                     ngx_http_limit_req_rbtree_insert_value);
0656 
0657     ngx_queue_init(&ctx->sh->queue);
0658 
0659     len = sizeof(" in limit_req zone \"\"") + shm_zone->shm.name.len;
0660 
0661     ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
0662     if (ctx->shpool->log_ctx == NULL) {
0663         return NGX_ERROR;
0664     }
0665 
0666     ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",
0667                 &shm_zone->shm.name);
0668 
0669     ctx->shpool->log_nomem = 0;
0670 
0671     return NGX_OK;
0672 }
0673 
0674 
0675 static void *
0676 ngx_http_limit_req_create_conf(ngx_conf_t *cf)
0677 {
0678     ngx_http_limit_req_conf_t  *conf;
0679 
0680     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));
0681     if (conf == NULL) {
0682         return NULL;
0683     }
0684 
0685     /*
0686      * set by ngx_pcalloc():
0687      *
0688      *     conf->limits.elts = NULL;
0689      */
0690 
0691     conf->limit_log_level = NGX_CONF_UNSET_UINT;
0692     conf->status_code = NGX_CONF_UNSET_UINT;
0693 
0694     return conf;
0695 }
0696 
0697 
0698 static char *
0699 ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
0700 {
0701     ngx_http_limit_req_conf_t *prev = parent;
0702     ngx_http_limit_req_conf_t *conf = child;
0703 
0704     if (conf->limits.elts == NULL) {
0705         conf->limits = prev->limits;
0706     }
0707 
0708     ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level,
0709                               NGX_LOG_ERR);
0710 
0711     conf->delay_log_level = (conf->limit_log_level == NGX_LOG_INFO) ?
0712                                 NGX_LOG_INFO : conf->limit_log_level + 1;
0713 
0714     ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
0715                               NGX_HTTP_SERVICE_UNAVAILABLE);
0716 
0717     return NGX_CONF_OK;
0718 }
0719 
0720 
0721 static char *
0722 ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0723 {
0724     u_char                            *p;
0725     size_t                             len;
0726     ssize_t                            size;
0727     ngx_str_t                         *value, name, s;
0728     ngx_int_t                          rate, scale;
0729     ngx_uint_t                         i;
0730     ngx_shm_zone_t                    *shm_zone;
0731     ngx_http_limit_req_ctx_t          *ctx;
0732     ngx_http_compile_complex_value_t   ccv;
0733 
0734     value = cf->args->elts;
0735 
0736     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));
0737     if (ctx == NULL) {
0738         return NGX_CONF_ERROR;
0739     }
0740 
0741     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
0742 
0743     ccv.cf = cf;
0744     ccv.value = &value[1];
0745     ccv.complex_value = &ctx->key;
0746 
0747     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
0748         return NGX_CONF_ERROR;
0749     }
0750 
0751     size = 0;
0752     rate = 1;
0753     scale = 1;
0754     name.len = 0;
0755 
0756     for (i = 2; i < cf->args->nelts; i++) {
0757 
0758         if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
0759 
0760             name.data = value[i].data + 5;
0761 
0762             p = (u_char *) ngx_strchr(name.data, ':');
0763 
0764             if (p == NULL) {
0765                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0766                                    "invalid zone size \"%V\"", &value[i]);
0767                 return NGX_CONF_ERROR;
0768             }
0769 
0770             name.len = p - name.data;
0771 
0772             s.data = p + 1;
0773             s.len = value[i].data + value[i].len - s.data;
0774 
0775             size = ngx_parse_size(&s);
0776 
0777             if (size == NGX_ERROR) {
0778                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0779                                    "invalid zone size \"%V\"", &value[i]);
0780                 return NGX_CONF_ERROR;
0781             }
0782 
0783             if (size < (ssize_t) (8 * ngx_pagesize)) {
0784                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0785                                    "zone \"%V\" is too small", &value[i]);
0786                 return NGX_CONF_ERROR;
0787             }
0788 
0789             continue;
0790         }
0791 
0792         if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
0793 
0794             len = value[i].len;
0795             p = value[i].data + len - 3;
0796 
0797             if (ngx_strncmp(p, "r/s", 3) == 0) {
0798                 scale = 1;
0799                 len -= 3;
0800 
0801             } else if (ngx_strncmp(p, "r/m", 3) == 0) {
0802                 scale = 60;
0803                 len -= 3;
0804             }
0805 
0806             rate = ngx_atoi(value[i].data + 5, len - 5);
0807             if (rate <= 0) {
0808                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0809                                    "invalid rate \"%V\"", &value[i]);
0810                 return NGX_CONF_ERROR;
0811             }
0812 
0813             continue;
0814         }
0815 
0816         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0817                            "invalid parameter \"%V\"", &value[i]);
0818         return NGX_CONF_ERROR;
0819     }
0820 
0821     if (name.len == 0) {
0822         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0823                            "\"%V\" must have \"zone\" parameter",
0824                            &cmd->name);
0825         return NGX_CONF_ERROR;
0826     }
0827 
0828     ctx->rate = rate * 1000 / scale;
0829 
0830     shm_zone = ngx_shared_memory_add(cf, &name, size,
0831                                      &ngx_http_limit_req_module);
0832     if (shm_zone == NULL) {
0833         return NGX_CONF_ERROR;
0834     }
0835 
0836     if (shm_zone->data) {
0837         ctx = shm_zone->data;
0838 
0839         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0840                            "%V \"%V\" is already bound to key \"%V\"",
0841                            &cmd->name, &name, &ctx->key.value);
0842         return NGX_CONF_ERROR;
0843     }
0844 
0845     shm_zone->init = ngx_http_limit_req_init_zone;
0846     shm_zone->data = ctx;
0847 
0848     return NGX_CONF_OK;
0849 }
0850 
0851 
0852 static char *
0853 ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0854 {
0855     ngx_http_limit_req_conf_t  *lrcf = conf;
0856 
0857     ngx_int_t                    burst;
0858     ngx_str_t                   *value, s;
0859     ngx_uint_t                   i, nodelay;
0860     ngx_shm_zone_t              *shm_zone;
0861     ngx_http_limit_req_limit_t  *limit, *limits;
0862 
0863     value = cf->args->elts;
0864 
0865     shm_zone = NULL;
0866     burst = 0;
0867     nodelay = 0;
0868 
0869     for (i = 1; i < cf->args->nelts; i++) {
0870 
0871         if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
0872 
0873             s.len = value[i].len - 5;
0874             s.data = value[i].data + 5;
0875 
0876             shm_zone = ngx_shared_memory_add(cf, &s, 0,
0877                                              &ngx_http_limit_req_module);
0878             if (shm_zone == NULL) {
0879                 return NGX_CONF_ERROR;
0880             }
0881 
0882             continue;
0883         }
0884 
0885         if (ngx_strncmp(value[i].data, "burst=", 6) == 0) {
0886 
0887             burst = ngx_atoi(value[i].data + 6, value[i].len - 6);
0888             if (burst <= 0) {
0889                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0890                                    "invalid burst rate \"%V\"", &value[i]);
0891                 return NGX_CONF_ERROR;
0892             }
0893 
0894             continue;
0895         }
0896 
0897         if (ngx_strcmp(value[i].data, "nodelay") == 0) {
0898             nodelay = 1;
0899             continue;
0900         }
0901 
0902         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0903                            "invalid parameter \"%V\"", &value[i]);
0904         return NGX_CONF_ERROR;
0905     }
0906 
0907     if (shm_zone == NULL) {
0908         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0909                            "\"%V\" must have \"zone\" parameter",
0910                            &cmd->name);
0911         return NGX_CONF_ERROR;
0912     }
0913 
0914     limits = lrcf->limits.elts;
0915 
0916     if (limits == NULL) {
0917         if (ngx_array_init(&lrcf->limits, cf->pool, 1,
0918                            sizeof(ngx_http_limit_req_limit_t))
0919             != NGX_OK)
0920         {
0921             return NGX_CONF_ERROR;
0922         }
0923     }
0924 
0925     for (i = 0; i < lrcf->limits.nelts; i++) {
0926         if (shm_zone == limits[i].shm_zone) {
0927             return "is duplicate";
0928         }
0929     }
0930 
0931     limit = ngx_array_push(&lrcf->limits);
0932     if (limit == NULL) {
0933         return NGX_CONF_ERROR;
0934     }
0935 
0936     limit->shm_zone = shm_zone;
0937     limit->burst = burst * 1000;
0938     limit->nodelay = nodelay;
0939 
0940     return NGX_CONF_OK;
0941 }
0942 
0943 
0944 static ngx_int_t
0945 ngx_http_limit_req_init(ngx_conf_t *cf)
0946 {
0947     ngx_http_handler_pt        *h;
0948     ngx_http_core_main_conf_t  *cmcf;
0949 
0950     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
0951 
0952     h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
0953     if (h == NULL) {
0954         return NGX_ERROR;
0955     }
0956 
0957     *h = ngx_http_limit_req_handler;
0958 
0959     return NGX_OK;
0960 }