src/http/modules/ngx_http_limit_conn_module.c - nginx-1.31.9 nginx/ @ 2a3a19c6f

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. ‌#define NGX_HTTP_LIMIT_CONN_PASSED            1
  9. ‌#define NGX_HTTP_LIMIT_CONN_REJECTED          2
  10. ‌#define NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN  3


  11. ‌typedef struct {
  12.     u_char                        color;
  13.     u_char                        len;
  14.     u_short                       conn;
  15.     u_char                        data[1];
  16. ‌} ngx_http_limit_conn_node_t;


  17. ‌typedef struct {
  18.     ngx_shm_zone_t               *shm_zone;
  19.     ngx_rbtree_node_t            *node;
  20. ‌} ngx_http_limit_conn_cleanup_t;


  21. ‌typedef struct {
  22.     ngx_rbtree_t                  rbtree;
  23.     ngx_rbtree_node_t             sentinel;
  24. ‌} ngx_http_limit_conn_shctx_t;


  25. ‌typedef struct {
  26.     ngx_http_limit_conn_shctx_t  *sh;
  27.     ngx_slab_pool_t              *shpool;
  28.     ngx_http_complex_value_t      key;
  29. ‌} ngx_http_limit_conn_ctx_t;


  30. ‌typedef struct {
  31.     ngx_shm_zone_t               *shm_zone;
  32.     ngx_uint_t                    conn;
  33. ‌} ngx_http_limit_conn_limit_t;


  34. ‌typedef struct {
  35.     ngx_array_t                   limits;
  36.     ngx_uint_t                    log_level;
  37.     ngx_uint_t                    status_code;
  38.     ngx_flag_t                    dry_run;
  39. ‌} ngx_http_limit_conn_conf_t;


  40. static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree,
  41.     ngx_str_t *key, uint32_t hash);
  42. static void ngx_http_limit_conn_cleanup(void *data);
  43. static ngx_inline void ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool);
  44. static void ngx_http_limit_conn_cleanup_node(ngx_shm_zone_t *shm_zone,
  45.     ngx_rbtree_node_t *node);

  46. static ngx_int_t ngx_http_limit_conn_status_variable(ngx_http_request_t *r,
  47.     ngx_http_variable_value_t *v, uintptr_t data);
  48. static void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf);
  49. static char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
  50.     void *child);
  51. static char *ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
  52.     void *conf);
  53. static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
  54.     void *conf);
  55. static ngx_int_t ngx_http_limit_conn_add_variables(ngx_conf_t *cf);
  56. static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf);


  57. ‌static ngx_conf_enum_t  ngx_http_limit_conn_log_levels[] = {
  58.     { ngx_string("info"), NGX_LOG_INFO },
  59.     { ngx_string("notice"), NGX_LOG_NOTICE },
  60.     { ngx_string("warn"), NGX_LOG_WARN },
  61.     { ngx_string("error"), NGX_LOG_ERR },
  62.     { ngx_null_string, 0 }
  63. };


  64. ‌static ngx_conf_num_bounds_t  ngx_http_limit_conn_status_bounds = {
  65.     ngx_conf_check_num_bounds, 400, 599
  66. };


  67. ‌static ngx_command_t  ngx_http_limit_conn_commands[] = {

  68.     { ngx_string("limit_conn_zone"),
  69.       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,
  70.       ngx_http_limit_conn_zone,
  71.       0,
  72.       0,
  73.       NULL },

  74.     { ngx_string("limit_conn"),
  75.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
  76.       ngx_http_limit_conn,
  77.       NGX_HTTP_LOC_CONF_OFFSET,
  78.       0,
  79.       NULL },

  80.     { ngx_string("limit_conn_log_level"),
  81.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  82.       ngx_conf_set_enum_slot,
  83.       NGX_HTTP_LOC_CONF_OFFSET,
  84.       offsetof(ngx_http_limit_conn_conf_t, log_level),
  85.       &ngx_http_limit_conn_log_levels },

  86.     { ngx_string("limit_conn_status"),
  87.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  88.       ngx_conf_set_num_slot,
  89.       NGX_HTTP_LOC_CONF_OFFSET,
  90.       offsetof(ngx_http_limit_conn_conf_t, status_code),
  91.       &ngx_http_limit_conn_status_bounds },

  92.     { ngx_string("limit_conn_dry_run"),
  93.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
  94.       ngx_conf_set_flag_slot,
  95.       NGX_HTTP_LOC_CONF_OFFSET,
  96.       offsetof(ngx_http_limit_conn_conf_t, dry_run),
  97.       NULL },

  98.       ngx_null_command
  99. };


  100. ‌static ngx_http_module_t  ngx_http_limit_conn_module_ctx = {
  101.     ngx_http_limit_conn_add_variables,     /* preconfiguration */
  102.     ngx_http_limit_conn_init,              /* postconfiguration */

  103.     NULL,                                  /* create main configuration */
  104.     NULL,                                  /* init main configuration */

  105.     NULL,                                  /* create server configuration */
  106.     NULL,                                  /* merge server configuration */

  107.     ngx_http_limit_conn_create_conf,       /* create location configuration */
  108.     ngx_http_limit_conn_merge_conf         /* merge location configuration */
  109. };


  110. ‌ngx_module_t  ngx_http_limit_conn_module = {
  111.     NGX_MODULE_V1,
  112.     &ngx_http_limit_conn_module_ctx,       /* module context */
  113.     ngx_http_limit_conn_commands,          /* module directives */
  114.     NGX_HTTP_MODULE,                       /* module type */
  115.     NULL,                                  /* init master */
  116.     NULL,                                  /* init module */
  117.     NULL,                                  /* init process */
  118.     NULL,                                  /* init thread */
  119.     NULL,                                  /* exit thread */
  120.     NULL,                                  /* exit process */
  121.     NULL,                                  /* exit master */
  122.     NGX_MODULE_V1_PADDING
  123. };


  124. ‌static ngx_http_variable_t  ngx_http_limit_conn_vars[] = {

  125.     { ngx_string("limit_conn_status"), NULL,
  126.       ngx_http_limit_conn_status_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

  127.       ngx_http_null_variable
  128. };


  129. ‌static ngx_str_t  ngx_http_limit_conn_status[] = {
  130.     ngx_string("PASSED"),
  131.     ngx_string("REJECTED"),
  132.     ngx_string("REJECTED_DRY_RUN")
  133. };


  134. static ngx_int_t
  135. ‌ngx_http_limit_conn_handler(ngx_http_request_t *r)
  136. {
  137.     size_t                          n;
  138.     uint32_t                        hash;
  139.     ngx_str_t                       key;
  140.     ngx_uint_t                      i;
  141.     ngx_rbtree_node_t              *node;
  142.     ngx_pool_cleanup_t             *cln;
  143.     ngx_http_limit_conn_ctx_t      *ctx;
  144.     ngx_http_limit_conn_node_t     *lc;
  145.     ngx_http_limit_conn_conf_t     *lccf;
  146.     ngx_http_limit_conn_limit_t    *limits;
  147.     ngx_http_limit_conn_cleanup_t  *lccln;

  148.     if (r->main->limit_conn_status) {
  149.         return NGX_DECLINED;
  150.     }

  151.     lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module);
  152.     limits = lccf->limits.elts;

  153.     for (i = 0; i < lccf->limits.nelts; i++) {
  154.         ctx = limits[i].shm_zone->data;

  155.         if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
  156.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  157.         }

  158.         if (key.len == 0) {
  159.             continue;
  160.         }

  161.         if (key.len > 255) {
  162.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  163.                           "the value of the \"%V\" key "
  164.                           "is more than 255 bytes: \"%V\"",
  165.                           &ctx->key.value, &key);
  166.             continue;
  167.         }

  168.         r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_PASSED;

  169.         hash = ngx_crc32_short(key.data, key.len);

  170.         ngx_shmtx_lock(&ctx->shpool->mutex);

  171.         node = ngx_http_limit_conn_lookup(&ctx->sh->rbtree, &key, hash);

  172.         if (node == NULL) {

  173.             n = offsetof(ngx_rbtree_node_t, color)
  174.                 + offsetof(ngx_http_limit_conn_node_t, data)
  175.                 + key.len;

  176.             node = ngx_slab_alloc_locked(ctx->shpool, n);

  177.             if (node == NULL) {
  178.                 ngx_shmtx_unlock(&ctx->shpool->mutex);
  179.                 ngx_http_limit_conn_cleanup_all(r->pool);

  180.                 if (lccf->dry_run) {
  181.                     r->main->limit_conn_status =
  182.                                           NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN;
  183.                     return NGX_DECLINED;
  184.                 }

  185.                 r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_REJECTED;

  186.                 return lccf->status_code;
  187.             }

  188.             lc = (ngx_http_limit_conn_node_t *) &node->color;

  189.             node->key = hash;
  190.             lc->len = (u_char) key.len;
  191.             lc->conn = 1;
  192.             ngx_memcpy(lc->data, key.data, key.len);

  193.             ngx_rbtree_insert(&ctx->sh->rbtree, node);

  194.         } else {

  195.             lc = (ngx_http_limit_conn_node_t *) &node->color;

  196.             if ((ngx_uint_t) lc->conn >= limits[i].conn) {

  197.                 ngx_shmtx_unlock(&ctx->shpool->mutex);

  198.                 ngx_log_error(lccf->log_level, r->connection->log, 0,
  199.                               "limiting connections%s by zone \"%V\"",
  200.                               lccf->dry_run ? ", dry run," : "",
  201.                               &limits[i].shm_zone->shm.name);

  202.                 ngx_http_limit_conn_cleanup_all(r->pool);

  203.                 if (lccf->dry_run) {
  204.                     r->main->limit_conn_status =
  205.                                           NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN;
  206.                     return NGX_DECLINED;
  207.                 }

  208.                 r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_REJECTED;

  209.                 return lccf->status_code;
  210.             }

  211.             lc->conn++;
  212.         }

  213.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  214.                        "limit conn: %08Xi %d", node->key, lc->conn);

  215.         ngx_shmtx_unlock(&ctx->shpool->mutex);

  216.         cln = ngx_pool_cleanup_add(r->pool,
  217.                                    sizeof(ngx_http_limit_conn_cleanup_t));
  218.         if (cln == NULL) {
  219.             ngx_http_limit_conn_cleanup_node(limits[i].shm_zone, node);
  220.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  221.         }

  222.         cln->handler = ngx_http_limit_conn_cleanup;
  223.         lccln = cln->data;

  224.         lccln->shm_zone = limits[i].shm_zone;
  225.         lccln->node = node;
  226.     }

  227.     return NGX_DECLINED;
  228. }


  229. static void
  230. ‌ngx_http_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp,
  231.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
  232. {
  233.     ngx_rbtree_node_t           **p;
  234.     ngx_http_limit_conn_node_t   *lcn, *lcnt;

  235.     for ( ;; ) {

  236.         if (node->key < temp->key) {

  237.             p = &temp->left;

  238.         } else if (node->key > temp->key) {

  239.             p = &temp->right;

  240.         } else { /* node->key == temp->key */

  241.             lcn = (ngx_http_limit_conn_node_t *) &node->color;
  242.             lcnt = (ngx_http_limit_conn_node_t *) &temp->color;

  243.             p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)
  244.                 ? &temp->left : &temp->right;
  245.         }

  246.         if (*p == sentinel) {
  247.             break;
  248.         }

  249.         temp = *p;
  250.     }

  251.     *p = node;
  252.     node->parent = temp;
  253.     node->left = sentinel;
  254.     node->right = sentinel;
  255.     ngx_rbt_red(node);
  256. }


  257. static ngx_rbtree_node_t *
  258. ‌ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key, uint32_t hash)
  259. {
  260.     ngx_int_t                    rc;
  261.     ngx_rbtree_node_t           *node, *sentinel;
  262.     ngx_http_limit_conn_node_t  *lcn;

  263.     node = rbtree->root;
  264.     sentinel = rbtree->sentinel;

  265.     while (node != sentinel) {

  266.         if (hash < node->key) {
  267.             node = node->left;
  268.             continue;
  269.         }

  270.         if (hash > node->key) {
  271.             node = node->right;
  272.             continue;
  273.         }

  274.         /* hash == node->key */

  275.         lcn = (ngx_http_limit_conn_node_t *) &node->color;

  276.         rc = ngx_memn2cmp(key->data, lcn->data, key->len, (size_t) lcn->len);

  277.         if (rc == 0) {
  278.             return node;
  279.         }

  280.         node = (rc < 0) ? node->left : node->right;
  281.     }

  282.     return NULL;
  283. }


  284. static void
  285. ‌ngx_http_limit_conn_cleanup(void *data)
  286. {
  287.     ngx_http_limit_conn_cleanup_t  *lccln = data;

  288.     ngx_rbtree_node_t           *node;
  289.     ngx_http_limit_conn_ctx_t   *ctx;
  290.     ngx_http_limit_conn_node_t  *lc;

  291.     ctx = lccln->shm_zone->data;
  292.     node = lccln->node;
  293.     lc = (ngx_http_limit_conn_node_t *) &node->color;

  294.     ngx_shmtx_lock(&ctx->shpool->mutex);

  295.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0,
  296.                    "limit conn cleanup: %08Xi %d", node->key, lc->conn);

  297.     lc->conn--;

  298.     if (lc->conn == 0) {
  299.         ngx_rbtree_delete(&ctx->sh->rbtree, node);
  300.         ngx_slab_free_locked(ctx->shpool, node);
  301.     }

  302.     ngx_shmtx_unlock(&ctx->shpool->mutex);
  303. }


  304. static ngx_inline void
  305. ‌ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool)
  306. {
  307.     ngx_pool_cleanup_t  *cln;

  308.     cln = pool->cleanup;

  309.     while (cln && cln->handler == ngx_http_limit_conn_cleanup) {
  310.         ngx_http_limit_conn_cleanup(cln->data);
  311.         cln = cln->next;
  312.     }

  313.     pool->cleanup = cln;
  314. }


  315. static void
  316. ‌ngx_http_limit_conn_cleanup_node(ngx_shm_zone_t *shm_zone,
  317.     ngx_rbtree_node_t *node)
  318. {
  319.     ngx_http_limit_conn_cleanup_t  lccln;

  320.     lccln.shm_zone = shm_zone;
  321.     lccln.node = node;

  322.     ngx_http_limit_conn_cleanup(&lccln);
  323. }


  324. static ngx_int_t
  325. ‌ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
  326. {
  327.     ngx_http_limit_conn_ctx_t  *octx = data;

  328.     size_t                      len;
  329.     ngx_http_limit_conn_ctx_t  *ctx;

  330.     ctx = shm_zone->data;

  331.     if (octx) {
  332.         if (ctx->key.value.len != octx->key.value.len
  333.             || ngx_strncmp(ctx->key.value.data, octx->key.value.data,
  334.                            ctx->key.value.len)
  335.                != 0)
  336.         {
  337.             ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
  338.                           "limit_conn_zone \"%V\" uses the \"%V\" key "
  339.                           "while previously it used the \"%V\" key",
  340.                           &shm_zone->shm.name, &ctx->key.value,
  341.                           &octx->key.value);
  342.             return NGX_ERROR;
  343.         }

  344.         ctx->sh = octx->sh;
  345.         ctx->shpool = octx->shpool;

  346.         return NGX_OK;
  347.     }

  348.     ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;

  349.     if (shm_zone->shm.exists) {
  350.         ctx->sh = ctx->shpool->data;

  351.         return NGX_OK;
  352.     }

  353.     ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_conn_shctx_t));
  354.     if (ctx->sh == NULL) {
  355.         return NGX_ERROR;
  356.     }

  357.     ctx->shpool->data = ctx->sh;

  358.     ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
  359.                     ngx_http_limit_conn_rbtree_insert_value);

  360.     len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len;

  361.     ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
  362.     if (ctx->shpool->log_ctx == NULL) {
  363.         return NGX_ERROR;
  364.     }

  365.     ngx_sprintf(ctx->shpool->log_ctx, " in limit_conn_zone \"%V\"%Z",
  366.                 &shm_zone->shm.name);

  367.     return NGX_OK;
  368. }


  369. static ngx_int_t
  370. ‌ngx_http_limit_conn_status_variable(ngx_http_request_t *r,
  371.     ngx_http_variable_value_t *v, uintptr_t data)
  372. {
  373.     if (r->main->limit_conn_status == 0) {
  374.         v->not_found = 1;
  375.         return NGX_OK;
  376.     }

  377.     v->valid = 1;
  378.     v->no_cacheable = 0;
  379.     v->not_found = 0;
  380.     v->len = ngx_http_limit_conn_status[r->main->limit_conn_status - 1].len;
  381.     v->data = ngx_http_limit_conn_status[r->main->limit_conn_status - 1].data;

  382.     return NGX_OK;
  383. }


  384. static void *
  385. ‌ngx_http_limit_conn_create_conf(ngx_conf_t *cf)
  386. {
  387.     ngx_http_limit_conn_conf_t  *conf;

  388.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_conf_t));
  389.     if (conf == NULL) {
  390.         return NULL;
  391.     }

  392.     /*
  393.      * set by ngx_pcalloc():
  394.      *
  395.      *     conf->limits.elts = NULL;
  396.      */

  397.     conf->log_level = NGX_CONF_UNSET_UINT;
  398.     conf->status_code = NGX_CONF_UNSET_UINT;
  399.     conf->dry_run = NGX_CONF_UNSET;

  400.     return conf;
  401. }


  402. static char *
  403. ‌ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  404. {
  405.     ngx_http_limit_conn_conf_t *prev = parent;
  406.     ngx_http_limit_conn_conf_t *conf = child;

  407.     if (conf->limits.elts == NULL) {
  408.         conf->limits = prev->limits;
  409.     }

  410.     ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);
  411.     ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
  412.                               NGX_HTTP_SERVICE_UNAVAILABLE);

  413.     ngx_conf_merge_value(conf->dry_run, prev->dry_run, 0);

  414.     return NGX_CONF_OK;
  415. }


  416. static char *
  417. ‌ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  418. {
  419.     u_char                            *p;
  420.     ssize_t                            size;
  421.     ngx_str_t                         *value, name, s;
  422.     ngx_uint_t                         i;
  423.     ngx_shm_zone_t                    *shm_zone;
  424.     ngx_http_limit_conn_ctx_t         *ctx;
  425.     ngx_http_compile_complex_value_t   ccv;

  426.     value = cf->args->elts;

  427.     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t));
  428.     if (ctx == NULL) {
  429.         return NGX_CONF_ERROR;
  430.     }

  431.     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

  432.     ccv.cf = cf;
  433.     ccv.value = &value[1];
  434.     ccv.complex_value = &ctx->key;

  435.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  436.         return NGX_CONF_ERROR;
  437.     }

  438.     size = 0;
  439.     name.len = 0;

  440.     for (i = 2; i < cf->args->nelts; i++) {

  441.         if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {

  442.             name.data = value[i].data + 5;

  443.             p = (u_char *) ngx_strchr(name.data, ':');

  444.             if (p == NULL) {
  445.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  446.                                    "invalid zone size \"%V\"", &value[i]);
  447.                 return NGX_CONF_ERROR;
  448.             }

  449.             name.len = p - name.data;

  450.             s.data = p + 1;
  451.             s.len = value[i].data + value[i].len - s.data;

  452.             size = ngx_parse_size(&s);

  453.             if (size == NGX_ERROR) {
  454.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  455.                                    "invalid zone size \"%V\"", &value[i]);
  456.                 return NGX_CONF_ERROR;
  457.             }

  458.             if (size < (ssize_t) (8 * ngx_pagesize)) {
  459.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  460.                                    "zone \"%V\" is too small", &value[i]);
  461.                 return NGX_CONF_ERROR;
  462.             }

  463.             continue;
  464.         }

  465.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  466.                            "invalid parameter \"%V\"", &value[i]);
  467.         return NGX_CONF_ERROR;
  468.     }

  469.     if (name.len == 0) {
  470.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  471.                            "\"%V\" must have \"zone\" parameter",
  472.                            &cmd->name);
  473.         return NGX_CONF_ERROR;
  474.     }

  475.     shm_zone = ngx_shared_memory_add(cf, &name, size,
  476.                                      &ngx_http_limit_conn_module);
  477.     if (shm_zone == NULL) {
  478.         return NGX_CONF_ERROR;
  479.     }

  480.     if (shm_zone->data) {
  481.         ctx = shm_zone->data;

  482.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  483.                            "%V \"%V\" is already bound to key \"%V\"",
  484.                            &cmd->name, &name, &ctx->key.value);
  485.         return NGX_CONF_ERROR;
  486.     }

  487.     shm_zone->init = ngx_http_limit_conn_init_zone;
  488.     shm_zone->data = ctx;

  489.     return NGX_CONF_OK;
  490. }


  491. static char *
  492. ‌ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  493. {
  494.     ngx_shm_zone_t               *shm_zone;
  495.     ngx_http_limit_conn_conf_t   *lccf = conf;
  496.     ngx_http_limit_conn_limit_t  *limit, *limits;

  497.     ngx_str_t  *value;
  498.     ngx_int_t   n;
  499.     ngx_uint_t  i;

  500.     value = cf->args->elts;

  501.     shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
  502.                                      &ngx_http_limit_conn_module);
  503.     if (shm_zone == NULL) {
  504.         return NGX_CONF_ERROR;
  505.     }

  506.     limits = lccf->limits.elts;

  507.     if (limits == NULL) {
  508.         if (ngx_array_init(&lccf->limits, cf->pool, 1,
  509.                            sizeof(ngx_http_limit_conn_limit_t))
  510.             != NGX_OK)
  511.         {
  512.             return NGX_CONF_ERROR;
  513.         }
  514.     }

  515.     for (i = 0; i < lccf->limits.nelts; i++) {
  516.         if (shm_zone == limits[i].shm_zone) {
  517.             return "is duplicate";
  518.         }
  519.     }

  520.     n = ngx_atoi(value[2].data, value[2].len);
  521.     if (n <= 0) {
  522.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  523.                            "invalid number of connections \"%V\"", &value[2]);
  524.         return NGX_CONF_ERROR;
  525.     }

  526.     if (n > 65535) {
  527.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  528.                            "connection limit must be less 65536");
  529.         return NGX_CONF_ERROR;
  530.     }

  531.     limit = ngx_array_push(&lccf->limits);
  532.     if (limit == NULL) {
  533.         return NGX_CONF_ERROR;
  534.     }

  535.     limit->conn = n;
  536.     limit->shm_zone = shm_zone;

  537.     return NGX_CONF_OK;
  538. }


  539. static ngx_int_t
  540. ‌ngx_http_limit_conn_add_variables(ngx_conf_t *cf)
  541. {
  542.     ngx_http_variable_t  *var, *v;

  543.     for (v = ngx_http_limit_conn_vars; v->name.len; v++) {
  544.         var = ngx_http_add_variable(cf, &v->name, v->flags);
  545.         if (var == NULL) {
  546.             return NGX_ERROR;
  547.         }

  548.         var->get_handler = v->get_handler;
  549.         var->data = v->data;
  550.     }

  551.     return NGX_OK;
  552. }


  553. static ngx_int_t
  554. ‌ngx_http_limit_conn_init(ngx_conf_t *cf)
  555. {
  556.     ngx_http_handler_pt        *h;
  557.     ngx_http_core_main_conf_t  *cmcf;

  558.     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

  559.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
  560.     if (h == NULL) {
  561.         return NGX_ERROR;
  562.     }

  563.     *h = ngx_http_limit_conn_handler;

  564.     return NGX_OK;
  565. }