src/stream/ngx_stream_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_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 ngx_int_t ngx_stream_limit_conn_status_variable(ngx_stream_session_t *s,
  44.     ngx_stream_variable_value_t *v, uintptr_t data);
  45. static void *ngx_stream_limit_conn_create_conf(ngx_conf_t *cf);
  46. static char *ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
  47.     void *child);
  48. static char *ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
  49.     void *conf);
  50. static char *ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
  51.     void *conf);
  52. static ngx_int_t ngx_stream_limit_conn_add_variables(ngx_conf_t *cf);
  53. static ngx_int_t ngx_stream_limit_conn_init(ngx_conf_t *cf);


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


  61. static ngx_command_t  ngx_stream_limit_conn_commands[] = {

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

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

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

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

  86.       ngx_null_command
  87. };


  88. static ngx_stream_module_t  ngx_stream_limit_conn_module_ctx = {
  89.     ngx_stream_limit_conn_add_variables,   /* preconfiguration */
  90.     ngx_stream_limit_conn_init,            /* postconfiguration */

  91.     NULL,                                  /* create main configuration */
  92.     NULL,                                  /* init main configuration */

  93.     ngx_stream_limit_conn_create_conf,     /* create server configuration */
  94.     ngx_stream_limit_conn_merge_conf       /* merge server configuration */
  95. };


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


  110. static ngx_stream_variable_t  ngx_stream_limit_conn_vars[] = {

  111.     { ngx_string("limit_conn_status"), NULL,
  112.       ngx_stream_limit_conn_status_variable, 0, NGX_STREAM_VAR_NOCACHEABLE, 0 },

  113.       ngx_stream_null_variable
  114. };


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


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

  134.     lccf = ngx_stream_get_module_srv_conf(s, ngx_stream_limit_conn_module);
  135.     limits = lccf->limits.elts;

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

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

  141.         if (key.len == 0) {
  142.             continue;
  143.         }

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

  151.         s->limit_conn_status = NGX_STREAM_LIMIT_CONN_PASSED;

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

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

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

  155.         if (node == NULL) {

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

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

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

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

  168.                 s->limit_conn_status = NGX_STREAM_LIMIT_CONN_REJECTED;

  169.                 return NGX_STREAM_SERVICE_UNAVAILABLE;
  170.             }

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

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

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

  177.         } else {

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

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

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

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

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

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

  191.                 s->limit_conn_status = NGX_STREAM_LIMIT_CONN_REJECTED;

  192.                 return NGX_STREAM_SERVICE_UNAVAILABLE;
  193.             }

  194.             lc->conn++;
  195.         }

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

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

  199.         cln = ngx_pool_cleanup_add(s->connection->pool,
  200.                                    sizeof(ngx_stream_limit_conn_cleanup_t));
  201.         if (cln == NULL) {
  202.             return NGX_ERROR;
  203.         }

  204.         cln->handler = ngx_stream_limit_conn_cleanup;
  205.         lccln = cln->data;

  206.         lccln->shm_zone = limits[i].shm_zone;
  207.         lccln->node = node;
  208.     }

  209.     return NGX_DECLINED;
  210. }


  211. static void
  212. ngx_stream_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp,
  213.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
  214. {
  215.     ngx_rbtree_node_t             **p;
  216.     ngx_stream_limit_conn_node_t   *lcn, *lcnt;

  217.     for ( ;; ) {

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

  219.             p = &temp->left;

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

  221.             p = &temp->right;

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

  223.             lcn = (ngx_stream_limit_conn_node_t *) &node->color;
  224.             lcnt = (ngx_stream_limit_conn_node_t *) &temp->color;

  225.             p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)
  226.                 ? &temp->left : &temp->right;
  227.         }

  228.         if (*p == sentinel) {
  229.             break;
  230.         }

  231.         temp = *p;
  232.     }

  233.     *p = node;
  234.     node->parent = temp;
  235.     node->left = sentinel;
  236.     node->right = sentinel;
  237.     ngx_rbt_red(node);
  238. }


  239. static ngx_rbtree_node_t *
  240. ngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key,
  241.     uint32_t hash)
  242. {
  243.     ngx_int_t                      rc;
  244.     ngx_rbtree_node_t             *node, *sentinel;
  245.     ngx_stream_limit_conn_node_t  *lcn;

  246.     node = rbtree->root;
  247.     sentinel = rbtree->sentinel;

  248.     while (node != sentinel) {

  249.         if (hash < node->key) {
  250.             node = node->left;
  251.             continue;
  252.         }

  253.         if (hash > node->key) {
  254.             node = node->right;
  255.             continue;
  256.         }

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

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

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

  260.         if (rc == 0) {
  261.             return node;
  262.         }

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

  265.     return NULL;
  266. }


  267. static void
  268. ngx_stream_limit_conn_cleanup(void *data)
  269. {
  270.     ngx_stream_limit_conn_cleanup_t  *lccln = data;

  271.     ngx_rbtree_node_t             *node;
  272.     ngx_stream_limit_conn_ctx_t   *ctx;
  273.     ngx_stream_limit_conn_node_t  *lc;

  274.     ctx = lccln->shm_zone->data;
  275.     node = lccln->node;
  276.     lc = (ngx_stream_limit_conn_node_t *) &node->color;

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

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

  280.     lc->conn--;

  281.     if (lc->conn == 0) {
  282.         ngx_rbtree_delete(&ctx->sh->rbtree, node);
  283.         ngx_slab_free_locked(ctx->shpool, node);
  284.     }

  285.     ngx_shmtx_unlock(&ctx->shpool->mutex);
  286. }


  287. static ngx_inline void
  288. ngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool)
  289. {
  290.     ngx_pool_cleanup_t  *cln;

  291.     cln = pool->cleanup;

  292.     while (cln && cln->handler == ngx_stream_limit_conn_cleanup) {
  293.         ngx_stream_limit_conn_cleanup(cln->data);
  294.         cln = cln->next;
  295.     }

  296.     pool->cleanup = cln;
  297. }


  298. static ngx_int_t
  299. ngx_stream_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
  300. {
  301.     ngx_stream_limit_conn_ctx_t  *octx = data;

  302.     size_t                        len;
  303.     ngx_stream_limit_conn_ctx_t  *ctx;

  304.     ctx = shm_zone->data;

  305.     if (octx) {
  306.         if (ctx->key.value.len != octx->key.value.len
  307.             || ngx_strncmp(ctx->key.value.data, octx->key.value.data,
  308.                            ctx->key.value.len)
  309.                != 0)
  310.         {
  311.             ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
  312.                           "limit_conn_zone \"%V\" uses the \"%V\" key "
  313.                           "while previously it used the \"%V\" key",
  314.                           &shm_zone->shm.name, &ctx->key.value,
  315.                           &octx->key.value);
  316.             return NGX_ERROR;
  317.         }

  318.         ctx->sh = octx->sh;
  319.         ctx->shpool = octx->shpool;

  320.         return NGX_OK;
  321.     }

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

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

  325.         return NGX_OK;
  326.     }

  327.     ctx->sh = ngx_slab_alloc(ctx->shpool,
  328.                              sizeof(ngx_stream_limit_conn_shctx_t));
  329.     if (ctx->sh == NULL) {
  330.         return NGX_ERROR;
  331.     }

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

  333.     ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
  334.                     ngx_stream_limit_conn_rbtree_insert_value);

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

  336.     ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
  337.     if (ctx->shpool->log_ctx == NULL) {
  338.         return NGX_ERROR;
  339.     }

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

  342.     return NGX_OK;
  343. }


  344. static ngx_int_t
  345. ngx_stream_limit_conn_status_variable(ngx_stream_session_t *s,
  346.     ngx_stream_variable_value_t *v, uintptr_t data)
  347. {
  348.     if (s->limit_conn_status == 0) {
  349.         v->not_found = 1;
  350.         return NGX_OK;
  351.     }

  352.     v->valid = 1;
  353.     v->no_cacheable = 0;
  354.     v->not_found = 0;
  355.     v->len = ngx_stream_limit_conn_status[s->limit_conn_status - 1].len;
  356.     v->data = ngx_stream_limit_conn_status[s->limit_conn_status - 1].data;

  357.     return NGX_OK;
  358. }


  359. static void *
  360. ngx_stream_limit_conn_create_conf(ngx_conf_t *cf)
  361. {
  362.     ngx_stream_limit_conn_conf_t  *conf;

  363.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_limit_conn_conf_t));
  364.     if (conf == NULL) {
  365.         return NULL;
  366.     }

  367.     /*
  368.      * set by ngx_pcalloc():
  369.      *
  370.      *     conf->limits.elts = NULL;
  371.      */

  372.     conf->log_level = NGX_CONF_UNSET_UINT;
  373.     conf->dry_run = NGX_CONF_UNSET;

  374.     return conf;
  375. }


  376. static char *
  377. ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  378. {
  379.     ngx_stream_limit_conn_conf_t *prev = parent;
  380.     ngx_stream_limit_conn_conf_t *conf = child;

  381.     if (conf->limits.elts == NULL) {
  382.         conf->limits = prev->limits;
  383.     }

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

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

  386.     return NGX_CONF_OK;
  387. }


  388. static char *
  389. ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  390. {
  391.     u_char                              *p;
  392.     ssize_t                              size;
  393.     ngx_str_t                           *value, name, s;
  394.     ngx_uint_t                           i;
  395.     ngx_shm_zone_t                      *shm_zone;
  396.     ngx_stream_limit_conn_ctx_t         *ctx;
  397.     ngx_stream_compile_complex_value_t   ccv;

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

  399.     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_limit_conn_ctx_t));
  400.     if (ctx == NULL) {
  401.         return NGX_CONF_ERROR;
  402.     }

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

  404.     ccv.cf = cf;
  405.     ccv.value = &value[1];
  406.     ccv.complex_value = &ctx->key;

  407.     if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
  408.         return NGX_CONF_ERROR;
  409.     }

  410.     size = 0;
  411.     name.len = 0;

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

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

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

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

  416.             if (p == NULL) {
  417.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  418.                                    "invalid zone size \"%V\"", &value[i]);
  419.                 return NGX_CONF_ERROR;
  420.             }

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

  422.             s.data = p + 1;
  423.             s.len = value[i].data + value[i].len - s.data;

  424.             size = ngx_parse_size(&s);

  425.             if (size == NGX_ERROR) {
  426.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  427.                                    "invalid zone size \"%V\"", &value[i]);
  428.                 return NGX_CONF_ERROR;
  429.             }

  430.             if (size < (ssize_t) (8 * ngx_pagesize)) {
  431.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  432.                                    "zone \"%V\" is too small", &value[i]);
  433.                 return NGX_CONF_ERROR;
  434.             }

  435.             continue;
  436.         }

  437.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  438.                            "invalid parameter \"%V\"", &value[i]);
  439.         return NGX_CONF_ERROR;
  440.     }

  441.     if (name.len == 0) {
  442.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  443.                            "\"%V\" must have \"zone\" parameter",
  444.                            &cmd->name);
  445.         return NGX_CONF_ERROR;
  446.     }

  447.     shm_zone = ngx_shared_memory_add(cf, &name, size,
  448.                                      &ngx_stream_limit_conn_module);
  449.     if (shm_zone == NULL) {
  450.         return NGX_CONF_ERROR;
  451.     }

  452.     if (shm_zone->data) {
  453.         ctx = shm_zone->data;

  454.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  455.                            "%V \"%V\" is already bound to key \"%V\"",
  456.                            &cmd->name, &name, &ctx->key.value);
  457.         return NGX_CONF_ERROR;
  458.     }

  459.     shm_zone->init = ngx_stream_limit_conn_init_zone;
  460.     shm_zone->data = ctx;

  461.     return NGX_CONF_OK;
  462. }


  463. static char *
  464. ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  465. {
  466.     ngx_shm_zone_t                 *shm_zone;
  467.     ngx_stream_limit_conn_conf_t   *lccf = conf;
  468.     ngx_stream_limit_conn_limit_t  *limit, *limits;

  469.     ngx_str_t   *value;
  470.     ngx_int_t    n;
  471.     ngx_uint_t   i;

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

  473.     shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
  474.                                      &ngx_stream_limit_conn_module);
  475.     if (shm_zone == NULL) {
  476.         return NGX_CONF_ERROR;
  477.     }

  478.     limits = lccf->limits.elts;

  479.     if (limits == NULL) {
  480.         if (ngx_array_init(&lccf->limits, cf->pool, 1,
  481.                            sizeof(ngx_stream_limit_conn_limit_t))
  482.             != NGX_OK)
  483.         {
  484.             return NGX_CONF_ERROR;
  485.         }
  486.     }

  487.     for (i = 0; i < lccf->limits.nelts; i++) {
  488.         if (shm_zone == limits[i].shm_zone) {
  489.             return "is duplicate";
  490.         }
  491.     }

  492.     n = ngx_atoi(value[2].data, value[2].len);
  493.     if (n <= 0) {
  494.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  495.                            "invalid number of connections \"%V\"", &value[2]);
  496.         return NGX_CONF_ERROR;
  497.     }

  498.     if (n > 65535) {
  499.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  500.                            "connection limit must be less 65536");
  501.         return NGX_CONF_ERROR;
  502.     }

  503.     limit = ngx_array_push(&lccf->limits);
  504.     if (limit == NULL) {
  505.         return NGX_CONF_ERROR;
  506.     }

  507.     limit->conn = n;
  508.     limit->shm_zone = shm_zone;

  509.     return NGX_CONF_OK;
  510. }


  511. static ngx_int_t
  512. ngx_stream_limit_conn_add_variables(ngx_conf_t *cf)
  513. {
  514.     ngx_stream_variable_t  *var, *v;

  515.     for (v = ngx_stream_limit_conn_vars; v->name.len; v++) {
  516.         var = ngx_stream_add_variable(cf, &v->name, v->flags);
  517.         if (var == NULL) {
  518.             return NGX_ERROR;
  519.         }

  520.         var->get_handler = v->get_handler;
  521.         var->data = v->data;
  522.     }

  523.     return NGX_OK;
  524. }


  525. static ngx_int_t
  526. ngx_stream_limit_conn_init(ngx_conf_t *cf)
  527. {
  528.     ngx_stream_handler_pt        *h;
  529.     ngx_stream_core_main_conf_t  *cmcf;

  530.     cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);

  531.     h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers);
  532.     if (h == NULL) {
  533.         return NGX_ERROR;
  534.     }

  535.     *h = ngx_stream_limit_conn_handler;

  536.     return NGX_OK;
  537. }