Back to home page

Nginx displayed by LXR

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