Annotation of embedaddon/nginx/src/http/modules/ngx_http_limit_req_module.c, revision 1.1.1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>