Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

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                   delay;
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             if (ms < -60000) {
0403                 ms = 1;
0404 
0405             } else if (ms < 0) {
0406                 ms = 0;
0407             }
0408 
0409             excess = lr->excess - ctx->rate * ms / 1000 + 1000;
0410 
0411             if (excess < 0) {
0412                 excess = 0;
0413             }
0414 
0415             *ep = excess;
0416 
0417             if ((ngx_uint_t) excess > limit->burst) {
0418                 return NGX_BUSY;
0419             }
0420 
0421             if (account) {
0422                 lr->excess = excess;
0423 
0424                 if (ms) {
0425                     lr->last = now;
0426                 }
0427 
0428                 return NGX_OK;
0429             }
0430 
0431             lr->count++;
0432 
0433             ctx->node = lr;
0434 
0435             return NGX_AGAIN;
0436         }
0437 
0438         node = (rc < 0) ? node->left : node->right;
0439     }
0440 
0441     *ep = 0;
0442 
0443     size = offsetof(ngx_rbtree_node_t, color)
0444            + offsetof(ngx_http_limit_req_node_t, data)
0445            + key->len;
0446 
0447     ngx_http_limit_req_expire(ctx, 1);
0448 
0449     node = ngx_slab_alloc_locked(ctx->shpool, size);
0450 
0451     if (node == NULL) {
0452         ngx_http_limit_req_expire(ctx, 0);
0453 
0454         node = ngx_slab_alloc_locked(ctx->shpool, size);
0455         if (node == NULL) {
0456             ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
0457                           "could not allocate node%s", ctx->shpool->log_ctx);
0458             return NGX_ERROR;
0459         }
0460     }
0461 
0462     node->key = hash;
0463 
0464     lr = (ngx_http_limit_req_node_t *) &node->color;
0465 
0466     lr->len = (u_short) key->len;
0467     lr->excess = 0;
0468 
0469     ngx_memcpy(lr->data, key->data, key->len);
0470 
0471     ngx_rbtree_insert(&ctx->sh->rbtree, node);
0472 
0473     ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
0474 
0475     if (account) {
0476         lr->last = now;
0477         lr->count = 0;
0478         return NGX_OK;
0479     }
0480 
0481     lr->last = 0;
0482     lr->count = 1;
0483 
0484     ctx->node = lr;
0485 
0486     return NGX_AGAIN;
0487 }
0488 
0489 
0490 static ngx_msec_t
0491 ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n,
0492     ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit)
0493 {
0494     ngx_int_t                   excess;
0495     ngx_msec_t                  now, delay, max_delay;
0496     ngx_msec_int_t              ms;
0497     ngx_http_limit_req_ctx_t   *ctx;
0498     ngx_http_limit_req_node_t  *lr;
0499 
0500     excess = *ep;
0501 
0502     if ((ngx_uint_t) excess <= (*limit)->delay) {
0503         max_delay = 0;
0504 
0505     } else {
0506         ctx = (*limit)->shm_zone->data;
0507         max_delay = (excess - (*limit)->delay) * 1000 / ctx->rate;
0508     }
0509 
0510     while (n--) {
0511         ctx = limits[n].shm_zone->data;
0512         lr = ctx->node;
0513 
0514         if (lr == NULL) {
0515             continue;
0516         }
0517 
0518         ngx_shmtx_lock(&ctx->shpool->mutex);
0519 
0520         now = ngx_current_msec;
0521         ms = (ngx_msec_int_t) (now - lr->last);
0522 
0523         if (ms < -60000) {
0524             ms = 1;
0525 
0526         } else if (ms < 0) {
0527             ms = 0;
0528         }
0529 
0530         excess = lr->excess - ctx->rate * ms / 1000 + 1000;
0531 
0532         if (excess < 0) {
0533             excess = 0;
0534         }
0535 
0536         if (ms) {
0537             lr->last = now;
0538         }
0539 
0540         lr->excess = excess;
0541         lr->count--;
0542 
0543         ngx_shmtx_unlock(&ctx->shpool->mutex);
0544 
0545         ctx->node = NULL;
0546 
0547         if ((ngx_uint_t) excess <= limits[n].delay) {
0548             continue;
0549         }
0550 
0551         delay = (excess - limits[n].delay) * 1000 / ctx->rate;
0552 
0553         if (delay > max_delay) {
0554             max_delay = delay;
0555             *ep = excess;
0556             *limit = &limits[n];
0557         }
0558     }
0559 
0560     return max_delay;
0561 }
0562 
0563 
0564 static void
0565 ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
0566 {
0567     ngx_int_t                   excess;
0568     ngx_msec_t                  now;
0569     ngx_queue_t                *q;
0570     ngx_msec_int_t              ms;
0571     ngx_rbtree_node_t          *node;
0572     ngx_http_limit_req_node_t  *lr;
0573 
0574     now = ngx_current_msec;
0575 
0576     /*
0577      * n == 1 deletes one or two zero rate entries
0578      * n == 0 deletes oldest entry by force
0579      *        and one or two zero rate entries
0580      */
0581 
0582     while (n < 3) {
0583 
0584         if (ngx_queue_empty(&ctx->sh->queue)) {
0585             return;
0586         }
0587 
0588         q = ngx_queue_last(&ctx->sh->queue);
0589 
0590         lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
0591 
0592         if (lr->count) {
0593 
0594             /*
0595              * There is not much sense in looking further,
0596              * because we bump nodes on the lookup stage.
0597              */
0598 
0599             return;
0600         }
0601 
0602         if (n++ != 0) {
0603 
0604             ms = (ngx_msec_int_t) (now - lr->last);
0605             ms = ngx_abs(ms);
0606 
0607             if (ms < 60000) {
0608                 return;
0609             }
0610 
0611             excess = lr->excess - ctx->rate * ms / 1000;
0612 
0613             if (excess > 0) {
0614                 return;
0615             }
0616         }
0617 
0618         ngx_queue_remove(q);
0619 
0620         node = (ngx_rbtree_node_t *)
0621                    ((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
0622 
0623         ngx_rbtree_delete(&ctx->sh->rbtree, node);
0624 
0625         ngx_slab_free_locked(ctx->shpool, node);
0626     }
0627 }
0628 
0629 
0630 static ngx_int_t
0631 ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)
0632 {
0633     ngx_http_limit_req_ctx_t  *octx = data;
0634 
0635     size_t                     len;
0636     ngx_http_limit_req_ctx_t  *ctx;
0637 
0638     ctx = shm_zone->data;
0639 
0640     if (octx) {
0641         if (ctx->key.value.len != octx->key.value.len
0642             || ngx_strncmp(ctx->key.value.data, octx->key.value.data,
0643                            ctx->key.value.len)
0644                != 0)
0645         {
0646             ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
0647                           "limit_req \"%V\" uses the \"%V\" key "
0648                           "while previously it used the \"%V\" key",
0649                           &shm_zone->shm.name, &ctx->key.value,
0650                           &octx->key.value);
0651             return NGX_ERROR;
0652         }
0653 
0654         ctx->sh = octx->sh;
0655         ctx->shpool = octx->shpool;
0656 
0657         return NGX_OK;
0658     }
0659 
0660     ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
0661 
0662     if (shm_zone->shm.exists) {
0663         ctx->sh = ctx->shpool->data;
0664 
0665         return NGX_OK;
0666     }
0667 
0668     ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t));
0669     if (ctx->sh == NULL) {
0670         return NGX_ERROR;
0671     }
0672 
0673     ctx->shpool->data = ctx->sh;
0674 
0675     ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
0676                     ngx_http_limit_req_rbtree_insert_value);
0677 
0678     ngx_queue_init(&ctx->sh->queue);
0679 
0680     len = sizeof(" in limit_req zone \"\"") + shm_zone->shm.name.len;
0681 
0682     ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
0683     if (ctx->shpool->log_ctx == NULL) {
0684         return NGX_ERROR;
0685     }
0686 
0687     ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",
0688                 &shm_zone->shm.name);
0689 
0690     ctx->shpool->log_nomem = 0;
0691 
0692     return NGX_OK;
0693 }
0694 
0695 
0696 static void *
0697 ngx_http_limit_req_create_conf(ngx_conf_t *cf)
0698 {
0699     ngx_http_limit_req_conf_t  *conf;
0700 
0701     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));
0702     if (conf == NULL) {
0703         return NULL;
0704     }
0705 
0706     /*
0707      * set by ngx_pcalloc():
0708      *
0709      *     conf->limits.elts = NULL;
0710      */
0711 
0712     conf->limit_log_level = NGX_CONF_UNSET_UINT;
0713     conf->status_code = NGX_CONF_UNSET_UINT;
0714 
0715     return conf;
0716 }
0717 
0718 
0719 static char *
0720 ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
0721 {
0722     ngx_http_limit_req_conf_t *prev = parent;
0723     ngx_http_limit_req_conf_t *conf = child;
0724 
0725     if (conf->limits.elts == NULL) {
0726         conf->limits = prev->limits;
0727     }
0728 
0729     ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level,
0730                               NGX_LOG_ERR);
0731 
0732     conf->delay_log_level = (conf->limit_log_level == NGX_LOG_INFO) ?
0733                                 NGX_LOG_INFO : conf->limit_log_level + 1;
0734 
0735     ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
0736                               NGX_HTTP_SERVICE_UNAVAILABLE);
0737 
0738     return NGX_CONF_OK;
0739 }
0740 
0741 
0742 static char *
0743 ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0744 {
0745     u_char                            *p;
0746     size_t                             len;
0747     ssize_t                            size;
0748     ngx_str_t                         *value, name, s;
0749     ngx_int_t                          rate, scale;
0750     ngx_uint_t                         i;
0751     ngx_shm_zone_t                    *shm_zone;
0752     ngx_http_limit_req_ctx_t          *ctx;
0753     ngx_http_compile_complex_value_t   ccv;
0754 
0755     value = cf->args->elts;
0756 
0757     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));
0758     if (ctx == NULL) {
0759         return NGX_CONF_ERROR;
0760     }
0761 
0762     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
0763 
0764     ccv.cf = cf;
0765     ccv.value = &value[1];
0766     ccv.complex_value = &ctx->key;
0767 
0768     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
0769         return NGX_CONF_ERROR;
0770     }
0771 
0772     size = 0;
0773     rate = 1;
0774     scale = 1;
0775     name.len = 0;
0776 
0777     for (i = 2; i < cf->args->nelts; i++) {
0778 
0779         if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
0780 
0781             name.data = value[i].data + 5;
0782 
0783             p = (u_char *) ngx_strchr(name.data, ':');
0784 
0785             if (p == NULL) {
0786                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0787                                    "invalid zone size \"%V\"", &value[i]);
0788                 return NGX_CONF_ERROR;
0789             }
0790 
0791             name.len = p - name.data;
0792 
0793             s.data = p + 1;
0794             s.len = value[i].data + value[i].len - s.data;
0795 
0796             size = ngx_parse_size(&s);
0797 
0798             if (size == NGX_ERROR) {
0799                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0800                                    "invalid zone size \"%V\"", &value[i]);
0801                 return NGX_CONF_ERROR;
0802             }
0803 
0804             if (size < (ssize_t) (8 * ngx_pagesize)) {
0805                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0806                                    "zone \"%V\" is too small", &value[i]);
0807                 return NGX_CONF_ERROR;
0808             }
0809 
0810             continue;
0811         }
0812 
0813         if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
0814 
0815             len = value[i].len;
0816             p = value[i].data + len - 3;
0817 
0818             if (ngx_strncmp(p, "r/s", 3) == 0) {
0819                 scale = 1;
0820                 len -= 3;
0821 
0822             } else if (ngx_strncmp(p, "r/m", 3) == 0) {
0823                 scale = 60;
0824                 len -= 3;
0825             }
0826 
0827             rate = ngx_atoi(value[i].data + 5, len - 5);
0828             if (rate <= 0) {
0829                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0830                                    "invalid rate \"%V\"", &value[i]);
0831                 return NGX_CONF_ERROR;
0832             }
0833 
0834             continue;
0835         }
0836 
0837         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0838                            "invalid parameter \"%V\"", &value[i]);
0839         return NGX_CONF_ERROR;
0840     }
0841 
0842     if (name.len == 0) {
0843         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0844                            "\"%V\" must have \"zone\" parameter",
0845                            &cmd->name);
0846         return NGX_CONF_ERROR;
0847     }
0848 
0849     ctx->rate = rate * 1000 / scale;
0850 
0851     shm_zone = ngx_shared_memory_add(cf, &name, size,
0852                                      &ngx_http_limit_req_module);
0853     if (shm_zone == NULL) {
0854         return NGX_CONF_ERROR;
0855     }
0856 
0857     if (shm_zone->data) {
0858         ctx = shm_zone->data;
0859 
0860         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0861                            "%V \"%V\" is already bound to key \"%V\"",
0862                            &cmd->name, &name, &ctx->key.value);
0863         return NGX_CONF_ERROR;
0864     }
0865 
0866     shm_zone->init = ngx_http_limit_req_init_zone;
0867     shm_zone->data = ctx;
0868 
0869     return NGX_CONF_OK;
0870 }
0871 
0872 
0873 static char *
0874 ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0875 {
0876     ngx_http_limit_req_conf_t  *lrcf = conf;
0877 
0878     ngx_int_t                    burst, delay;
0879     ngx_str_t                   *value, s;
0880     ngx_uint_t                   i;
0881     ngx_shm_zone_t              *shm_zone;
0882     ngx_http_limit_req_limit_t  *limit, *limits;
0883 
0884     value = cf->args->elts;
0885 
0886     shm_zone = NULL;
0887     burst = 0;
0888     delay = 0;
0889 
0890     for (i = 1; i < cf->args->nelts; i++) {
0891 
0892         if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
0893 
0894             s.len = value[i].len - 5;
0895             s.data = value[i].data + 5;
0896 
0897             shm_zone = ngx_shared_memory_add(cf, &s, 0,
0898                                              &ngx_http_limit_req_module);
0899             if (shm_zone == NULL) {
0900                 return NGX_CONF_ERROR;
0901             }
0902 
0903             continue;
0904         }
0905 
0906         if (ngx_strncmp(value[i].data, "burst=", 6) == 0) {
0907 
0908             burst = ngx_atoi(value[i].data + 6, value[i].len - 6);
0909             if (burst <= 0) {
0910                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0911                                    "invalid burst value \"%V\"", &value[i]);
0912                 return NGX_CONF_ERROR;
0913             }
0914 
0915             continue;
0916         }
0917 
0918         if (ngx_strncmp(value[i].data, "delay=", 6) == 0) {
0919 
0920             delay = ngx_atoi(value[i].data + 6, value[i].len - 6);
0921             if (delay <= 0) {
0922                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0923                                    "invalid delay value \"%V\"", &value[i]);
0924                 return NGX_CONF_ERROR;
0925             }
0926 
0927             continue;
0928         }
0929 
0930         if (ngx_strcmp(value[i].data, "nodelay") == 0) {
0931             delay = NGX_MAX_INT_T_VALUE / 1000;
0932             continue;
0933         }
0934 
0935         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0936                            "invalid parameter \"%V\"", &value[i]);
0937         return NGX_CONF_ERROR;
0938     }
0939 
0940     if (shm_zone == NULL) {
0941         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0942                            "\"%V\" must have \"zone\" parameter",
0943                            &cmd->name);
0944         return NGX_CONF_ERROR;
0945     }
0946 
0947     limits = lrcf->limits.elts;
0948 
0949     if (limits == NULL) {
0950         if (ngx_array_init(&lrcf->limits, cf->pool, 1,
0951                            sizeof(ngx_http_limit_req_limit_t))
0952             != NGX_OK)
0953         {
0954             return NGX_CONF_ERROR;
0955         }
0956     }
0957 
0958     for (i = 0; i < lrcf->limits.nelts; i++) {
0959         if (shm_zone == limits[i].shm_zone) {
0960             return "is duplicate";
0961         }
0962     }
0963 
0964     limit = ngx_array_push(&lrcf->limits);
0965     if (limit == NULL) {
0966         return NGX_CONF_ERROR;
0967     }
0968 
0969     limit->shm_zone = shm_zone;
0970     limit->burst = burst * 1000;
0971     limit->delay = delay * 1000;
0972 
0973     return NGX_CONF_OK;
0974 }
0975 
0976 
0977 static ngx_int_t
0978 ngx_http_limit_req_init(ngx_conf_t *cf)
0979 {
0980     ngx_http_handler_pt        *h;
0981     ngx_http_core_main_conf_t  *cmcf;
0982 
0983     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
0984 
0985     h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
0986     if (h == NULL) {
0987         return NGX_ERROR;
0988     }
0989 
0990     *h = ngx_http_limit_req_handler;
0991 
0992     return NGX_OK;
0993 }