src/http/modules/ngx_http_limit_conn_module.c - nginx source code

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 ngx_int_t ngx_http_limit_conn_status_variable(ngx_http_request_t *r,
  45.     ngx_http_variable_value_t *v, uintptr_t data);
  46. static void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf);
  47. static char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
  48.     void *child);
  49. static char *ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
  50.     void *conf);
  51. static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
  52.     void *conf);
  53. static ngx_int_t ngx_http_limit_conn_add_variables(ngx_conf_t *cf);
  54. static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf);


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


  62. static ngx_conf_num_bounds_t  ngx_http_limit_conn_status_bounds = {
  63.     ngx_conf_check_num_bounds, 400, 599
  64. };


  65. static ngx_command_t  ngx_http_limit_conn_commands[] = {

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

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

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

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

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

  96.       ngx_null_command
  97. };


  98. static ngx_http_module_t  ngx_http_limit_conn_module_ctx = {
  99.     ngx_http_limit_conn_add_variables,     /* preconfiguration */
  100.     ngx_http_limit_conn_init,              /* postconfiguration */

  101.     NULL,                                  /* create main configuration */
  102.     NULL,                                  /* init main configuration */

  103.     NULL,                                  /* create server configuration */
  104.     NULL,                                  /* merge server configuration */

  105.     ngx_http_limit_conn_create_conf,       /* create location configuration */
  106.     ngx_http_limit_conn_merge_conf         /* merge location configuration */
  107. };


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


  122. static ngx_http_variable_t  ngx_http_limit_conn_vars[] = {

  123.     { ngx_string("limit_conn_status"), NULL,
  124.       ngx_http_limit_conn_status_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },

  125.       ngx_http_null_variable
  126. };


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


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

  146.     if (r->main->limit_conn_status) {
  147.         return NGX_DECLINED;
  148.     }

  149.     lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module);
  150.     limits = lccf->limits.elts;

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

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

  156.         if (key.len == 0) {
  157.             continue;
  158.         }

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

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

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

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

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

  170.         if (node == NULL) {

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

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

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

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

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

  184.                 return lccf->status_code;
  185.             }

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

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

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

  192.         } else {

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

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

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

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

  200.                 ngx_http_limit_conn_cleanup_all(r->pool);

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

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

  207.                 return lccf->status_code;
  208.             }

  209.             lc->conn++;
  210.         }

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

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

  214.         cln = ngx_pool_cleanup_add(r->pool,
  215.                                    sizeof(ngx_http_limit_conn_cleanup_t));
  216.         if (cln == NULL) {
  217.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  218.         }

  219.         cln->handler = ngx_http_limit_conn_cleanup;
  220.         lccln = cln->data;

  221.         lccln->shm_zone = limits[i].shm_zone;
  222.         lccln->node = node;
  223.     }

  224.     return NGX_DECLINED;
  225. }


  226. static void
  227. ngx_http_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp,
  228.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
  229. {
  230.     ngx_rbtree_node_t           **p;
  231.     ngx_http_limit_conn_node_t   *lcn, *lcnt;

  232.     for ( ;; ) {

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

  234.             p = &temp->left;

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

  236.             p = &temp->right;

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

  238.             lcn = (ngx_http_limit_conn_node_t *) &node->color;
  239.             lcnt = (ngx_http_limit_conn_node_t *) &temp->color;

  240.             p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)
  241.                 ? &temp->left : &temp->right;
  242.         }

  243.         if (*p == sentinel) {
  244.             break;
  245.         }

  246.         temp = *p;
  247.     }

  248.     *p = node;
  249.     node->parent = temp;
  250.     node->left = sentinel;
  251.     node->right = sentinel;
  252.     ngx_rbt_red(node);
  253. }


  254. static ngx_rbtree_node_t *
  255. ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key, uint32_t hash)
  256. {
  257.     ngx_int_t                    rc;
  258.     ngx_rbtree_node_t           *node, *sentinel;
  259.     ngx_http_limit_conn_node_t  *lcn;

  260.     node = rbtree->root;
  261.     sentinel = rbtree->sentinel;

  262.     while (node != sentinel) {

  263.         if (hash < node->key) {
  264.             node = node->left;
  265.             continue;
  266.         }

  267.         if (hash > node->key) {
  268.             node = node->right;
  269.             continue;
  270.         }

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

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

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

  274.         if (rc == 0) {
  275.             return node;
  276.         }

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

  279.     return NULL;
  280. }


  281. static void
  282. ngx_http_limit_conn_cleanup(void *data)
  283. {
  284.     ngx_http_limit_conn_cleanup_t  *lccln = data;

  285.     ngx_rbtree_node_t           *node;
  286.     ngx_http_limit_conn_ctx_t   *ctx;
  287.     ngx_http_limit_conn_node_t  *lc;

  288.     ctx = lccln->shm_zone->data;
  289.     node = lccln->node;
  290.     lc = (ngx_http_limit_conn_node_t *) &node->color;

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

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

  294.     lc->conn--;

  295.     if (lc->conn == 0) {
  296.         ngx_rbtree_delete(&ctx->sh->rbtree, node);
  297.         ngx_slab_free_locked(ctx->shpool, node);
  298.     }

  299.     ngx_shmtx_unlock(&ctx->shpool->mutex);
  300. }


  301. static ngx_inline void
  302. ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool)
  303. {
  304.     ngx_pool_cleanup_t  *cln;

  305.     cln = pool->cleanup;

  306.     while (cln && cln->handler == ngx_http_limit_conn_cleanup) {
  307.         ngx_http_limit_conn_cleanup(cln->data);
  308.         cln = cln->next;
  309.     }

  310.     pool->cleanup = cln;
  311. }


  312. static ngx_int_t
  313. ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
  314. {
  315.     ngx_http_limit_conn_ctx_t  *octx = data;

  316.     size_t                      len;
  317.     ngx_http_limit_conn_ctx_t  *ctx;

  318.     ctx = shm_zone->data;

  319.     if (octx) {
  320.         if (ctx->key.value.len != octx->key.value.len
  321.             || ngx_strncmp(ctx->key.value.data, octx->key.value.data,
  322.                            ctx->key.value.len)
  323.                != 0)
  324.         {
  325.             ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
  326.                           "limit_conn_zone \"%V\" uses the \"%V\" key "
  327.                           "while previously it used the \"%V\" key",
  328.                           &shm_zone->shm.name, &ctx->key.value,
  329.                           &octx->key.value);
  330.             return NGX_ERROR;
  331.         }

  332.         ctx->sh = octx->sh;
  333.         ctx->shpool = octx->shpool;

  334.         return NGX_OK;
  335.     }

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

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

  339.         return NGX_OK;
  340.     }

  341.     ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_conn_shctx_t));
  342.     if (ctx->sh == NULL) {
  343.         return NGX_ERROR;
  344.     }

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

  346.     ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
  347.                     ngx_http_limit_conn_rbtree_insert_value);

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

  349.     ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
  350.     if (ctx->shpool->log_ctx == NULL) {
  351.         return NGX_ERROR;
  352.     }

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

  355.     return NGX_OK;
  356. }


  357. static ngx_int_t
  358. ngx_http_limit_conn_status_variable(ngx_http_request_t *r,
  359.     ngx_http_variable_value_t *v, uintptr_t data)
  360. {
  361.     if (r->main->limit_conn_status == 0) {
  362.         v->not_found = 1;
  363.         return NGX_OK;
  364.     }

  365.     v->valid = 1;
  366.     v->no_cacheable = 0;
  367.     v->not_found = 0;
  368.     v->len = ngx_http_limit_conn_status[r->main->limit_conn_status - 1].len;
  369.     v->data = ngx_http_limit_conn_status[r->main->limit_conn_status - 1].data;

  370.     return NGX_OK;
  371. }


  372. static void *
  373. ngx_http_limit_conn_create_conf(ngx_conf_t *cf)
  374. {
  375.     ngx_http_limit_conn_conf_t  *conf;

  376.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_conf_t));
  377.     if (conf == NULL) {
  378.         return NULL;
  379.     }

  380.     /*
  381.      * set by ngx_pcalloc():
  382.      *
  383.      *     conf->limits.elts = NULL;
  384.      */

  385.     conf->log_level = NGX_CONF_UNSET_UINT;
  386.     conf->status_code = NGX_CONF_UNSET_UINT;
  387.     conf->dry_run = NGX_CONF_UNSET;

  388.     return conf;
  389. }


  390. static char *
  391. ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  392. {
  393.     ngx_http_limit_conn_conf_t *prev = parent;
  394.     ngx_http_limit_conn_conf_t *conf = child;

  395.     if (conf->limits.elts == NULL) {
  396.         conf->limits = prev->limits;
  397.     }

  398.     ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);
  399.     ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
  400.                               NGX_HTTP_SERVICE_UNAVAILABLE);

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

  402.     return NGX_CONF_OK;
  403. }


  404. static char *
  405. ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  406. {
  407.     u_char                            *p;
  408.     ssize_t                            size;
  409.     ngx_str_t                         *value, name, s;
  410.     ngx_uint_t                         i;
  411.     ngx_shm_zone_t                    *shm_zone;
  412.     ngx_http_limit_conn_ctx_t         *ctx;
  413.     ngx_http_compile_complex_value_t   ccv;

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

  415.     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t));
  416.     if (ctx == NULL) {
  417.         return NGX_CONF_ERROR;
  418.     }

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

  420.     ccv.cf = cf;
  421.     ccv.value = &value[1];
  422.     ccv.complex_value = &ctx->key;

  423.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  424.         return NGX_CONF_ERROR;
  425.     }

  426.     size = 0;
  427.     name.len = 0;

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

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

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

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

  432.             if (p == NULL) {
  433.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  434.                                    "invalid zone size \"%V\"", &value[i]);
  435.                 return NGX_CONF_ERROR;
  436.             }

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

  438.             s.data = p + 1;
  439.             s.len = value[i].data + value[i].len - s.data;

  440.             size = ngx_parse_size(&s);

  441.             if (size == NGX_ERROR) {
  442.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  443.                                    "invalid zone size \"%V\"", &value[i]);
  444.                 return NGX_CONF_ERROR;
  445.             }

  446.             if (size < (ssize_t) (8 * ngx_pagesize)) {
  447.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  448.                                    "zone \"%V\" is too small", &value[i]);
  449.                 return NGX_CONF_ERROR;
  450.             }

  451.             continue;
  452.         }

  453.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  454.                            "invalid parameter \"%V\"", &value[i]);
  455.         return NGX_CONF_ERROR;
  456.     }

  457.     if (name.len == 0) {
  458.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  459.                            "\"%V\" must have \"zone\" parameter",
  460.                            &cmd->name);
  461.         return NGX_CONF_ERROR;
  462.     }

  463.     shm_zone = ngx_shared_memory_add(cf, &name, size,
  464.                                      &ngx_http_limit_conn_module);
  465.     if (shm_zone == NULL) {
  466.         return NGX_CONF_ERROR;
  467.     }

  468.     if (shm_zone->data) {
  469.         ctx = shm_zone->data;

  470.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  471.                            "%V \"%V\" is already bound to key \"%V\"",
  472.                            &cmd->name, &name, &ctx->key.value);
  473.         return NGX_CONF_ERROR;
  474.     }

  475.     shm_zone->init = ngx_http_limit_conn_init_zone;
  476.     shm_zone->data = ctx;

  477.     return NGX_CONF_OK;
  478. }


  479. static char *
  480. ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  481. {
  482.     ngx_shm_zone_t               *shm_zone;
  483.     ngx_http_limit_conn_conf_t   *lccf = conf;
  484.     ngx_http_limit_conn_limit_t  *limit, *limits;

  485.     ngx_str_t  *value;
  486.     ngx_int_t   n;
  487.     ngx_uint_t  i;

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

  489.     shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
  490.                                      &ngx_http_limit_conn_module);
  491.     if (shm_zone == NULL) {
  492.         return NGX_CONF_ERROR;
  493.     }

  494.     limits = lccf->limits.elts;

  495.     if (limits == NULL) {
  496.         if (ngx_array_init(&lccf->limits, cf->pool, 1,
  497.                            sizeof(ngx_http_limit_conn_limit_t))
  498.             != NGX_OK)
  499.         {
  500.             return NGX_CONF_ERROR;
  501.         }
  502.     }

  503.     for (i = 0; i < lccf->limits.nelts; i++) {
  504.         if (shm_zone == limits[i].shm_zone) {
  505.             return "is duplicate";
  506.         }
  507.     }

  508.     n = ngx_atoi(value[2].data, value[2].len);
  509.     if (n <= 0) {
  510.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  511.                            "invalid number of connections \"%V\"", &value[2]);
  512.         return NGX_CONF_ERROR;
  513.     }

  514.     if (n > 65535) {
  515.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  516.                            "connection limit must be less 65536");
  517.         return NGX_CONF_ERROR;
  518.     }

  519.     limit = ngx_array_push(&lccf->limits);
  520.     if (limit == NULL) {
  521.         return NGX_CONF_ERROR;
  522.     }

  523.     limit->conn = n;
  524.     limit->shm_zone = shm_zone;

  525.     return NGX_CONF_OK;
  526. }


  527. static ngx_int_t
  528. ngx_http_limit_conn_add_variables(ngx_conf_t *cf)
  529. {
  530.     ngx_http_variable_t  *var, *v;

  531.     for (v = ngx_http_limit_conn_vars; v->name.len; v++) {
  532.         var = ngx_http_add_variable(cf, &v->name, v->flags);
  533.         if (var == NULL) {
  534.             return NGX_ERROR;
  535.         }

  536.         var->get_handler = v->get_handler;
  537.         var->data = v->data;
  538.     }

  539.     return NGX_OK;
  540. }


  541. static ngx_int_t
  542. ngx_http_limit_conn_init(ngx_conf_t *cf)
  543. {
  544.     ngx_http_handler_pt        *h;
  545.     ngx_http_core_main_conf_t  *cmcf;

  546.     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

  547.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
  548.     if (h == NULL) {
  549.         return NGX_ERROR;
  550.     }

  551.     *h = ngx_http_limit_conn_handler;

  552.     return NGX_OK;
  553. }