src/stream/ngx_stream_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_stream.h>


  8. ‌#define NGX_STREAM_LIMIT_CONN_PASSED            1
  9. ‌#define NGX_STREAM_LIMIT_CONN_REJECTED          2
  10. ‌#define NGX_STREAM_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_stream_limit_conn_node_t;


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


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


  25. ‌typedef struct {
  26.     ngx_stream_limit_conn_shctx_t  *sh;
  27.     ngx_slab_pool_t                *shpool;
  28.     ngx_stream_complex_value_t      key;
  29. ‌} ngx_stream_limit_conn_ctx_t;


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


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


  39. static ngx_rbtree_node_t *ngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree,
  40.     ngx_str_t *key, uint32_t hash);
  41. static void ngx_stream_limit_conn_cleanup(void *data);
  42. static ngx_inline void ngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool);
  43. static void ngx_stream_limit_conn_cleanup_node(ngx_shm_zone_t *shm_zone,
  44.     ngx_rbtree_node_t *node);

  45. static ngx_int_t ngx_stream_limit_conn_status_variable(ngx_stream_session_t *s,
  46.     ngx_stream_variable_value_t *v, uintptr_t data);
  47. static void *ngx_stream_limit_conn_create_conf(ngx_conf_t *cf);
  48. static char *ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
  49.     void *child);
  50. static char *ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
  51.     void *conf);
  52. static char *ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
  53.     void *conf);
  54. static ngx_int_t ngx_stream_limit_conn_add_variables(ngx_conf_t *cf);
  55. static ngx_int_t ngx_stream_limit_conn_init(ngx_conf_t *cf);


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


  63. ‌static ngx_command_t  ngx_stream_limit_conn_commands[] = {

  64.     { ngx_string("limit_conn_zone"),
  65.       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE2,
  66.       ngx_stream_limit_conn_zone,
  67.       0,
  68.       0,
  69.       NULL },

  70.     { ngx_string("limit_conn"),
  71.       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,
  72.       ngx_stream_limit_conn,
  73.       NGX_STREAM_SRV_CONF_OFFSET,
  74.       0,
  75.       NULL },

  76.     { ngx_string("limit_conn_log_level"),
  77.       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
  78.       ngx_conf_set_enum_slot,
  79.       NGX_STREAM_SRV_CONF_OFFSET,
  80.       offsetof(ngx_stream_limit_conn_conf_t, log_level),
  81.       &ngx_stream_limit_conn_log_levels },

  82.     { ngx_string("limit_conn_dry_run"),
  83.       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
  84.       ngx_conf_set_flag_slot,
  85.       NGX_STREAM_SRV_CONF_OFFSET,
  86.       offsetof(ngx_stream_limit_conn_conf_t, dry_run),
  87.       NULL },

  88.       ngx_null_command
  89. };


  90. ‌static ngx_stream_module_t  ngx_stream_limit_conn_module_ctx = {
  91.     ngx_stream_limit_conn_add_variables,   /* preconfiguration */
  92.     ngx_stream_limit_conn_init,            /* postconfiguration */

  93.     NULL,                                  /* create main configuration */
  94.     NULL,                                  /* init main configuration */

  95.     ngx_stream_limit_conn_create_conf,     /* create server configuration */
  96.     ngx_stream_limit_conn_merge_conf       /* merge server configuration */
  97. };


  98. ‌ngx_module_t  ngx_stream_limit_conn_module = {
  99.     NGX_MODULE_V1,
  100.     &ngx_stream_limit_conn_module_ctx,     /* module context */
  101.     ngx_stream_limit_conn_commands,        /* module directives */
  102.     NGX_STREAM_MODULE,                     /* module type */
  103.     NULL,                                  /* init master */
  104.     NULL,                                  /* init module */
  105.     NULL,                                  /* init process */
  106.     NULL,                                  /* init thread */
  107.     NULL,                                  /* exit thread */
  108.     NULL,                                  /* exit process */
  109.     NULL,                                  /* exit master */
  110.     NGX_MODULE_V1_PADDING
  111. };


  112. ‌static ngx_stream_variable_t  ngx_stream_limit_conn_vars[] = {

  113.     { ngx_string("limit_conn_status"), NULL,
  114.       ngx_stream_limit_conn_status_variable, 0, NGX_STREAM_VAR_NOCACHEABLE, 0 },

  115.       ngx_stream_null_variable
  116. };


  117. ‌static ngx_str_t  ngx_stream_limit_conn_status[] = {
  118.     ngx_string("PASSED"),
  119.     ngx_string("REJECTED"),
  120.     ngx_string("REJECTED_DRY_RUN")
  121. };


  122. static ngx_int_t
  123. ‌ngx_stream_limit_conn_handler(ngx_stream_session_t *s)
  124. {
  125.     size_t                            n;
  126.     uint32_t                          hash;
  127.     ngx_str_t                         key;
  128.     ngx_uint_t                        i;
  129.     ngx_rbtree_node_t                *node;
  130.     ngx_pool_cleanup_t               *cln;
  131.     ngx_stream_limit_conn_ctx_t      *ctx;
  132.     ngx_stream_limit_conn_node_t     *lc;
  133.     ngx_stream_limit_conn_conf_t     *lccf;
  134.     ngx_stream_limit_conn_limit_t    *limits;
  135.     ngx_stream_limit_conn_cleanup_t  *lccln;

  136.     lccf = ngx_stream_get_module_srv_conf(s, ngx_stream_limit_conn_module);
  137.     limits = lccf->limits.elts;

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

  140.         if (ngx_stream_complex_value(s, &ctx->key, &key) != NGX_OK) {
  141.             return NGX_ERROR;
  142.         }

  143.         if (key.len == 0) {
  144.             continue;
  145.         }

  146.         if (key.len > 255) {
  147.             ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
  148.                           "the value of the \"%V\" key "
  149.                           "is more than 255 bytes: \"%V\"",
  150.                           &ctx->key.value, &key);
  151.             continue;
  152.         }

  153.         s->limit_conn_status = NGX_STREAM_LIMIT_CONN_PASSED;

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

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

  156.         node = ngx_stream_limit_conn_lookup(&ctx->sh->rbtree, &key, hash);

  157.         if (node == NULL) {

  158.             n = offsetof(ngx_rbtree_node_t, color)
  159.                 + offsetof(ngx_stream_limit_conn_node_t, data)
  160.                 + key.len;

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

  162.             if (node == NULL) {
  163.                 ngx_shmtx_unlock(&ctx->shpool->mutex);
  164.                 ngx_stream_limit_conn_cleanup_all(s->connection->pool);

  165.                 if (lccf->dry_run) {
  166.                     s->limit_conn_status =
  167.                                         NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN;
  168.                     return NGX_DECLINED;
  169.                 }

  170.                 s->limit_conn_status = NGX_STREAM_LIMIT_CONN_REJECTED;

  171.                 return NGX_STREAM_SERVICE_UNAVAILABLE;
  172.             }

  173.             lc = (ngx_stream_limit_conn_node_t *) &node->color;

  174.             node->key = hash;
  175.             lc->len = (u_char) key.len;
  176.             lc->conn = 1;
  177.             ngx_memcpy(lc->data, key.data, key.len);

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

  179.         } else {

  180.             lc = (ngx_stream_limit_conn_node_t *) &node->color;

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

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

  183.                 ngx_log_error(lccf->log_level, s->connection->log, 0,
  184.                               "limiting connections%s by zone \"%V\"",
  185.                               lccf->dry_run ? ", dry run," : "",
  186.                               &limits[i].shm_zone->shm.name);

  187.                 ngx_stream_limit_conn_cleanup_all(s->connection->pool);

  188.                 if (lccf->dry_run) {
  189.                     s->limit_conn_status =
  190.                                         NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN;
  191.                     return NGX_DECLINED;
  192.                 }

  193.                 s->limit_conn_status = NGX_STREAM_LIMIT_CONN_REJECTED;

  194.                 return NGX_STREAM_SERVICE_UNAVAILABLE;
  195.             }

  196.             lc->conn++;
  197.         }

  198.         ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  199.                        "limit conn: %08Xi %d", node->key, lc->conn);

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

  201.         cln = ngx_pool_cleanup_add(s->connection->pool,
  202.                                    sizeof(ngx_stream_limit_conn_cleanup_t));
  203.         if (cln == NULL) {
  204.             ngx_stream_limit_conn_cleanup_node(limits[i].shm_zone, node);
  205.             return NGX_ERROR;
  206.         }

  207.         cln->handler = ngx_stream_limit_conn_cleanup;
  208.         lccln = cln->data;

  209.         lccln->shm_zone = limits[i].shm_zone;
  210.         lccln->node = node;
  211.     }

  212.     return NGX_DECLINED;
  213. }


  214. static void
  215. ‌ngx_stream_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp,
  216.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
  217. {
  218.     ngx_rbtree_node_t             **p;
  219.     ngx_stream_limit_conn_node_t   *lcn, *lcnt;

  220.     for ( ;; ) {

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

  222.             p = &temp->left;

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

  224.             p = &temp->right;

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

  226.             lcn = (ngx_stream_limit_conn_node_t *) &node->color;
  227.             lcnt = (ngx_stream_limit_conn_node_t *) &temp->color;

  228.             p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)
  229.                 ? &temp->left : &temp->right;
  230.         }

  231.         if (*p == sentinel) {
  232.             break;
  233.         }

  234.         temp = *p;
  235.     }

  236.     *p = node;
  237.     node->parent = temp;
  238.     node->left = sentinel;
  239.     node->right = sentinel;
  240.     ngx_rbt_red(node);
  241. }


  242. static ngx_rbtree_node_t *
  243. ‌ngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key,
  244.     uint32_t hash)
  245. {
  246.     ngx_int_t                      rc;
  247.     ngx_rbtree_node_t             *node, *sentinel;
  248.     ngx_stream_limit_conn_node_t  *lcn;

  249.     node = rbtree->root;
  250.     sentinel = rbtree->sentinel;

  251.     while (node != sentinel) {

  252.         if (hash < node->key) {
  253.             node = node->left;
  254.             continue;
  255.         }

  256.         if (hash > node->key) {
  257.             node = node->right;
  258.             continue;
  259.         }

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

  261.         lcn = (ngx_stream_limit_conn_node_t *) &node->color;

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

  263.         if (rc == 0) {
  264.             return node;
  265.         }

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

  268.     return NULL;
  269. }


  270. static void
  271. ‌ngx_stream_limit_conn_cleanup(void *data)
  272. {
  273.     ngx_stream_limit_conn_cleanup_t  *lccln = data;

  274.     ngx_rbtree_node_t             *node;
  275.     ngx_stream_limit_conn_ctx_t   *ctx;
  276.     ngx_stream_limit_conn_node_t  *lc;

  277.     ctx = lccln->shm_zone->data;
  278.     node = lccln->node;
  279.     lc = (ngx_stream_limit_conn_node_t *) &node->color;

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

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

  283.     lc->conn--;

  284.     if (lc->conn == 0) {
  285.         ngx_rbtree_delete(&ctx->sh->rbtree, node);
  286.         ngx_slab_free_locked(ctx->shpool, node);
  287.     }

  288.     ngx_shmtx_unlock(&ctx->shpool->mutex);
  289. }


  290. static ngx_inline void
  291. ‌ngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool)
  292. {
  293.     ngx_pool_cleanup_t  *cln;

  294.     cln = pool->cleanup;

  295.     while (cln && cln->handler == ngx_stream_limit_conn_cleanup) {
  296.         ngx_stream_limit_conn_cleanup(cln->data);
  297.         cln = cln->next;
  298.     }

  299.     pool->cleanup = cln;
  300. }


  301. static void
  302. ‌ngx_stream_limit_conn_cleanup_node(ngx_shm_zone_t *shm_zone,
  303.     ngx_rbtree_node_t *node)
  304. {
  305.     ngx_stream_limit_conn_cleanup_t  lccln;

  306.     lccln.shm_zone = shm_zone;
  307.     lccln.node = node;

  308.     ngx_stream_limit_conn_cleanup(&lccln);
  309. }


  310. static ngx_int_t
  311. ‌ngx_stream_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
  312. {
  313.     ngx_stream_limit_conn_ctx_t  *octx = data;

  314.     size_t                        len;
  315.     ngx_stream_limit_conn_ctx_t  *ctx;

  316.     ctx = shm_zone->data;

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

  330.         ctx->sh = octx->sh;
  331.         ctx->shpool = octx->shpool;

  332.         return NGX_OK;
  333.     }

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

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

  337.         return NGX_OK;
  338.     }

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

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

  345.     ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
  346.                     ngx_stream_limit_conn_rbtree_insert_value);

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

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

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

  354.     return NGX_OK;
  355. }


  356. static ngx_int_t
  357. ‌ngx_stream_limit_conn_status_variable(ngx_stream_session_t *s,
  358.     ngx_stream_variable_value_t *v, uintptr_t data)
  359. {
  360.     if (s->limit_conn_status == 0) {
  361.         v->not_found = 1;
  362.         return NGX_OK;
  363.     }

  364.     v->valid = 1;
  365.     v->no_cacheable = 0;
  366.     v->not_found = 0;
  367.     v->len = ngx_stream_limit_conn_status[s->limit_conn_status - 1].len;
  368.     v->data = ngx_stream_limit_conn_status[s->limit_conn_status - 1].data;

  369.     return NGX_OK;
  370. }


  371. static void *
  372. ‌ngx_stream_limit_conn_create_conf(ngx_conf_t *cf)
  373. {
  374.     ngx_stream_limit_conn_conf_t  *conf;

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

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

  384.     conf->log_level = NGX_CONF_UNSET_UINT;
  385.     conf->dry_run = NGX_CONF_UNSET;

  386.     return conf;
  387. }


  388. static char *
  389. ‌ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  390. {
  391.     ngx_stream_limit_conn_conf_t *prev = parent;
  392.     ngx_stream_limit_conn_conf_t *conf = child;

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

  396.     ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);

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

  398.     return NGX_CONF_OK;
  399. }


  400. static char *
  401. ‌ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  402. {
  403.     u_char                              *p;
  404.     ssize_t                              size;
  405.     ngx_str_t                           *value, name, s;
  406.     ngx_uint_t                           i;
  407.     ngx_shm_zone_t                      *shm_zone;
  408.     ngx_stream_limit_conn_ctx_t         *ctx;
  409.     ngx_stream_compile_complex_value_t   ccv;

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

  411.     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_limit_conn_ctx_t));
  412.     if (ctx == NULL) {
  413.         return NGX_CONF_ERROR;
  414.     }

  415.     ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));

  416.     ccv.cf = cf;
  417.     ccv.value = &value[1];
  418.     ccv.complex_value = &ctx->key;

  419.     if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
  420.         return NGX_CONF_ERROR;
  421.     }

  422.     size = 0;
  423.     name.len = 0;

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

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

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

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

  428.             if (p == NULL) {
  429.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  430.                                    "invalid zone size \"%V\"", &value[i]);
  431.                 return NGX_CONF_ERROR;
  432.             }

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

  434.             s.data = p + 1;
  435.             s.len = value[i].data + value[i].len - s.data;

  436.             size = ngx_parse_size(&s);

  437.             if (size == NGX_ERROR) {
  438.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  439.                                    "invalid zone size \"%V\"", &value[i]);
  440.                 return NGX_CONF_ERROR;
  441.             }

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

  447.             continue;
  448.         }

  449.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  450.                            "invalid parameter \"%V\"", &value[i]);
  451.         return NGX_CONF_ERROR;
  452.     }

  453.     if (name.len == 0) {
  454.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  455.                            "\"%V\" must have \"zone\" parameter",
  456.                            &cmd->name);
  457.         return NGX_CONF_ERROR;
  458.     }

  459.     shm_zone = ngx_shared_memory_add(cf, &name, size,
  460.                                      &ngx_stream_limit_conn_module);
  461.     if (shm_zone == NULL) {
  462.         return NGX_CONF_ERROR;
  463.     }

  464.     if (shm_zone->data) {
  465.         ctx = shm_zone->data;

  466.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  467.                            "%V \"%V\" is already bound to key \"%V\"",
  468.                            &cmd->name, &name, &ctx->key.value);
  469.         return NGX_CONF_ERROR;
  470.     }

  471.     shm_zone->init = ngx_stream_limit_conn_init_zone;
  472.     shm_zone->data = ctx;

  473.     return NGX_CONF_OK;
  474. }


  475. static char *
  476. ‌ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  477. {
  478.     ngx_shm_zone_t                 *shm_zone;
  479.     ngx_stream_limit_conn_conf_t   *lccf = conf;
  480.     ngx_stream_limit_conn_limit_t  *limit, *limits;

  481.     ngx_str_t   *value;
  482.     ngx_int_t    n;
  483.     ngx_uint_t   i;

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

  485.     shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
  486.                                      &ngx_stream_limit_conn_module);
  487.     if (shm_zone == NULL) {
  488.         return NGX_CONF_ERROR;
  489.     }

  490.     limits = lccf->limits.elts;

  491.     if (limits == NULL) {
  492.         if (ngx_array_init(&lccf->limits, cf->pool, 1,
  493.                            sizeof(ngx_stream_limit_conn_limit_t))
  494.             != NGX_OK)
  495.         {
  496.             return NGX_CONF_ERROR;
  497.         }
  498.     }

  499.     for (i = 0; i < lccf->limits.nelts; i++) {
  500.         if (shm_zone == limits[i].shm_zone) {
  501.             return "is duplicate";
  502.         }
  503.     }

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

  510.     if (n > 65535) {
  511.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  512.                            "connection limit must be less 65536");
  513.         return NGX_CONF_ERROR;
  514.     }

  515.     limit = ngx_array_push(&lccf->limits);
  516.     if (limit == NULL) {
  517.         return NGX_CONF_ERROR;
  518.     }

  519.     limit->conn = n;
  520.     limit->shm_zone = shm_zone;

  521.     return NGX_CONF_OK;
  522. }


  523. static ngx_int_t
  524. ‌ngx_stream_limit_conn_add_variables(ngx_conf_t *cf)
  525. {
  526.     ngx_stream_variable_t  *var, *v;

  527.     for (v = ngx_stream_limit_conn_vars; v->name.len; v++) {
  528.         var = ngx_stream_add_variable(cf, &v->name, v->flags);
  529.         if (var == NULL) {
  530.             return NGX_ERROR;
  531.         }

  532.         var->get_handler = v->get_handler;
  533.         var->data = v->data;
  534.     }

  535.     return NGX_OK;
  536. }


  537. static ngx_int_t
  538. ‌ngx_stream_limit_conn_init(ngx_conf_t *cf)
  539. {
  540.     ngx_stream_handler_pt        *h;
  541.     ngx_stream_core_main_conf_t  *cmcf;

  542.     cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);

  543.     h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers);
  544.     if (h == NULL) {
  545.         return NGX_ERROR;
  546.     }

  547.     *h = ngx_stream_limit_conn_handler;

  548.     return NGX_OK;
  549. }