1e18a033bSKonstantin Ananyev
2e18a033bSKonstantin Ananyev/*
3e18a033bSKonstantin Ananyev * Copyright (C) Igor Sysoev
4e18a033bSKonstantin Ananyev * Copyright (C) Nginx, Inc.
5e18a033bSKonstantin Ananyev */
6e18a033bSKonstantin Ananyev
7e18a033bSKonstantin Ananyev
8e18a033bSKonstantin Ananyev#include <ngx_config.h>
9e18a033bSKonstantin Ananyev#include <ngx_core.h>
10e18a033bSKonstantin Ananyev#include <ngx_stream.h>
11e18a033bSKonstantin Ananyev
12e18a033bSKonstantin Ananyev
13e18a033bSKonstantin Ananyevtypedef struct {
14e18a033bSKonstantin Ananyev    u_char                       color;
15e18a033bSKonstantin Ananyev    u_char                       len;
16e18a033bSKonstantin Ananyev    u_short                      conn;
17e18a033bSKonstantin Ananyev    u_char                       data[1];
18e18a033bSKonstantin Ananyev} ngx_stream_limit_conn_node_t;
19e18a033bSKonstantin Ananyev
20e18a033bSKonstantin Ananyev
21e18a033bSKonstantin Ananyevtypedef struct {
22e18a033bSKonstantin Ananyev    ngx_shm_zone_t              *shm_zone;
23e18a033bSKonstantin Ananyev    ngx_rbtree_node_t           *node;
24e18a033bSKonstantin Ananyev} ngx_stream_limit_conn_cleanup_t;
25e18a033bSKonstantin Ananyev
26e18a033bSKonstantin Ananyev
27e18a033bSKonstantin Ananyevtypedef struct {
28e18a033bSKonstantin Ananyev    ngx_rbtree_t                *rbtree;
29e18a033bSKonstantin Ananyev    ngx_stream_complex_value_t   key;
30e18a033bSKonstantin Ananyev} ngx_stream_limit_conn_ctx_t;
31e18a033bSKonstantin Ananyev
32e18a033bSKonstantin Ananyev
33e18a033bSKonstantin Ananyevtypedef struct {
34e18a033bSKonstantin Ananyev    ngx_shm_zone_t              *shm_zone;
35e18a033bSKonstantin Ananyev    ngx_uint_t                   conn;
36e18a033bSKonstantin Ananyev} ngx_stream_limit_conn_limit_t;
37e18a033bSKonstantin Ananyev
38e18a033bSKonstantin Ananyev
39e18a033bSKonstantin Ananyevtypedef struct {
40e18a033bSKonstantin Ananyev    ngx_array_t                  limits;
41e18a033bSKonstantin Ananyev    ngx_uint_t                   log_level;
42e18a033bSKonstantin Ananyev} ngx_stream_limit_conn_conf_t;
43e18a033bSKonstantin Ananyev
44e18a033bSKonstantin Ananyev
45e18a033bSKonstantin Ananyevstatic ngx_rbtree_node_t *ngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree,
46e18a033bSKonstantin Ananyev    ngx_str_t *key, uint32_t hash);
47e18a033bSKonstantin Ananyevstatic void ngx_stream_limit_conn_cleanup(void *data);
48e18a033bSKonstantin Ananyevstatic ngx_inline void ngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool);
49e18a033bSKonstantin Ananyev
50e18a033bSKonstantin Ananyevstatic void *ngx_stream_limit_conn_create_conf(ngx_conf_t *cf);
51e18a033bSKonstantin Ananyevstatic char *ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
52e18a033bSKonstantin Ananyev    void *child);
53e18a033bSKonstantin Ananyevstatic char *ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
54e18a033bSKonstantin Ananyev    void *conf);
55e18a033bSKonstantin Ananyevstatic char *ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
56e18a033bSKonstantin Ananyev    void *conf);
57e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_stream_limit_conn_init(ngx_conf_t *cf);
58e18a033bSKonstantin Ananyev
59e18a033bSKonstantin Ananyev
60e18a033bSKonstantin Ananyevstatic ngx_conf_enum_t  ngx_stream_limit_conn_log_levels[] = {
61e18a033bSKonstantin Ananyev    { ngx_string("info"), NGX_LOG_INFO },
62e18a033bSKonstantin Ananyev    { ngx_string("notice"), NGX_LOG_NOTICE },
63e18a033bSKonstantin Ananyev    { ngx_string("warn"), NGX_LOG_WARN },
64e18a033bSKonstantin Ananyev    { ngx_string("error"), NGX_LOG_ERR },
65e18a033bSKonstantin Ananyev    { ngx_null_string, 0 }
66e18a033bSKonstantin Ananyev};
67e18a033bSKonstantin Ananyev
68e18a033bSKonstantin Ananyev
69e18a033bSKonstantin Ananyevstatic ngx_command_t  ngx_stream_limit_conn_commands[] = {
70e18a033bSKonstantin Ananyev
71e18a033bSKonstantin Ananyev    { ngx_string("limit_conn_zone"),
72e18a033bSKonstantin Ananyev      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE2,
73e18a033bSKonstantin Ananyev      ngx_stream_limit_conn_zone,
74e18a033bSKonstantin Ananyev      0,
75e18a033bSKonstantin Ananyev      0,
76e18a033bSKonstantin Ananyev      NULL },
77e18a033bSKonstantin Ananyev
78e18a033bSKonstantin Ananyev    { ngx_string("limit_conn"),
79e18a033bSKonstantin Ananyev      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,
80e18a033bSKonstantin Ananyev      ngx_stream_limit_conn,
81e18a033bSKonstantin Ananyev      NGX_STREAM_SRV_CONF_OFFSET,
82e18a033bSKonstantin Ananyev      0,
83e18a033bSKonstantin Ananyev      NULL },
84e18a033bSKonstantin Ananyev
85e18a033bSKonstantin Ananyev    { ngx_string("limit_conn_log_level"),
86e18a033bSKonstantin Ananyev      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
87e18a033bSKonstantin Ananyev      ngx_conf_set_enum_slot,
88e18a033bSKonstantin Ananyev      NGX_STREAM_SRV_CONF_OFFSET,
89e18a033bSKonstantin Ananyev      offsetof(ngx_stream_limit_conn_conf_t, log_level),
90e18a033bSKonstantin Ananyev      &ngx_stream_limit_conn_log_levels },
91e18a033bSKonstantin Ananyev
92e18a033bSKonstantin Ananyev      ngx_null_command
93e18a033bSKonstantin Ananyev};
94e18a033bSKonstantin Ananyev
95e18a033bSKonstantin Ananyev
96e18a033bSKonstantin Ananyevstatic ngx_stream_module_t  ngx_stream_limit_conn_module_ctx = {
97e18a033bSKonstantin Ananyev    NULL,                                  /* preconfiguration */
98e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_init,            /* postconfiguration */
99e18a033bSKonstantin Ananyev
100e18a033bSKonstantin Ananyev    NULL,                                  /* create main configuration */
101e18a033bSKonstantin Ananyev    NULL,                                  /* init main configuration */
102e18a033bSKonstantin Ananyev
103e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_create_conf,     /* create server configuration */
104e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_merge_conf       /* merge server configuration */
105e18a033bSKonstantin Ananyev};
106e18a033bSKonstantin Ananyev
107e18a033bSKonstantin Ananyev
108e18a033bSKonstantin Ananyevngx_module_t  ngx_stream_limit_conn_module = {
109e18a033bSKonstantin Ananyev    NGX_MODULE_V1,
110e18a033bSKonstantin Ananyev    &ngx_stream_limit_conn_module_ctx,     /* module context */
111e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_commands,        /* module directives */
112e18a033bSKonstantin Ananyev    NGX_STREAM_MODULE,                     /* module type */
113e18a033bSKonstantin Ananyev    NULL,                                  /* init master */
114e18a033bSKonstantin Ananyev    NULL,                                  /* init module */
115e18a033bSKonstantin Ananyev    NULL,                                  /* init process */
116e18a033bSKonstantin Ananyev    NULL,                                  /* init thread */
117e18a033bSKonstantin Ananyev    NULL,                                  /* exit thread */
118e18a033bSKonstantin Ananyev    NULL,                                  /* exit process */
119e18a033bSKonstantin Ananyev    NULL,                                  /* exit master */
120e18a033bSKonstantin Ananyev    NGX_MODULE_V1_PADDING
121e18a033bSKonstantin Ananyev};
122e18a033bSKonstantin Ananyev
123e18a033bSKonstantin Ananyev
124e18a033bSKonstantin Ananyevstatic ngx_int_t
125e18a033bSKonstantin Ananyevngx_stream_limit_conn_handler(ngx_stream_session_t *s)
126e18a033bSKonstantin Ananyev{
127e18a033bSKonstantin Ananyev    size_t                            n;
128e18a033bSKonstantin Ananyev    uint32_t                          hash;
129e18a033bSKonstantin Ananyev    ngx_str_t                         key;
130e18a033bSKonstantin Ananyev    ngx_uint_t                        i;
131e18a033bSKonstantin Ananyev    ngx_slab_pool_t                  *shpool;
132e18a033bSKonstantin Ananyev    ngx_rbtree_node_t                *node;
133e18a033bSKonstantin Ananyev    ngx_pool_cleanup_t               *cln;
134e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_ctx_t      *ctx;
135e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_node_t     *lc;
136e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_conf_t     *lccf;
137e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_limit_t    *limits;
138e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_cleanup_t  *lccln;
139e18a033bSKonstantin Ananyev
140e18a033bSKonstantin Ananyev    lccf = ngx_stream_get_module_srv_conf(s, ngx_stream_limit_conn_module);
141e18a033bSKonstantin Ananyev    limits = lccf->limits.elts;
142e18a033bSKonstantin Ananyev
143e18a033bSKonstantin Ananyev    for (i = 0; i < lccf->limits.nelts; i++) {
144e18a033bSKonstantin Ananyev        ctx = limits[i].shm_zone->data;
145e18a033bSKonstantin Ananyev
146e18a033bSKonstantin Ananyev        if (ngx_stream_complex_value(s, &ctx->key, &key) != NGX_OK) {
147e18a033bSKonstantin Ananyev            return NGX_ERROR;
148e18a033bSKonstantin Ananyev        }
149e18a033bSKonstantin Ananyev
150e18a033bSKonstantin Ananyev        if (key.len == 0) {
151e18a033bSKonstantin Ananyev            continue;
152e18a033bSKonstantin Ananyev        }
153e18a033bSKonstantin Ananyev
154e18a033bSKonstantin Ananyev        if (key.len > 255) {
155e18a033bSKonstantin Ananyev            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
156e18a033bSKonstantin Ananyev                          "the value of the \"%V\" key "
157e18a033bSKonstantin Ananyev                          "is more than 255 bytes: \"%V\"",
158e18a033bSKonstantin Ananyev                          &ctx->key.value, &key);
159e18a033bSKonstantin Ananyev            continue;
160e18a033bSKonstantin Ananyev        }
161e18a033bSKonstantin Ananyev
162e18a033bSKonstantin Ananyev        hash = ngx_crc32_short(key.data, key.len);
163e18a033bSKonstantin Ananyev
164e18a033bSKonstantin Ananyev        shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr;
165e18a033bSKonstantin Ananyev
166e18a033bSKonstantin Ananyev        ngx_shmtx_lock(&shpool->mutex);
167e18a033bSKonstantin Ananyev
168e18a033bSKonstantin Ananyev        node = ngx_stream_limit_conn_lookup(ctx->rbtree, &key, hash);
169e18a033bSKonstantin Ananyev
170e18a033bSKonstantin Ananyev        if (node == NULL) {
171e18a033bSKonstantin Ananyev
172e18a033bSKonstantin Ananyev            n = offsetof(ngx_rbtree_node_t, color)
173e18a033bSKonstantin Ananyev                + offsetof(ngx_stream_limit_conn_node_t, data)
174e18a033bSKonstantin Ananyev                + key.len;
175e18a033bSKonstantin Ananyev
176e18a033bSKonstantin Ananyev            node = ngx_slab_alloc_locked(shpool, n);
177e18a033bSKonstantin Ananyev
178e18a033bSKonstantin Ananyev            if (node == NULL) {
179e18a033bSKonstantin Ananyev                ngx_shmtx_unlock(&shpool->mutex);
180e18a033bSKonstantin Ananyev                ngx_stream_limit_conn_cleanup_all(s->connection->pool);
181e18a033bSKonstantin Ananyev                return NGX_STREAM_SERVICE_UNAVAILABLE;
182e18a033bSKonstantin Ananyev            }
183e18a033bSKonstantin Ananyev
184e18a033bSKonstantin Ananyev            lc = (ngx_stream_limit_conn_node_t *) &node->color;
185e18a033bSKonstantin Ananyev
186e18a033bSKonstantin Ananyev            node->key = hash;
187e18a033bSKonstantin Ananyev            lc->len = (u_char) key.len;
188e18a033bSKonstantin Ananyev            lc->conn = 1;
189e18a033bSKonstantin Ananyev            ngx_memcpy(lc->data, key.data, key.len);
190e18a033bSKonstantin Ananyev
191e18a033bSKonstantin Ananyev            ngx_rbtree_insert(ctx->rbtree, node);
192e18a033bSKonstantin Ananyev
193e18a033bSKonstantin Ananyev        } else {
194e18a033bSKonstantin Ananyev
195e18a033bSKonstantin Ananyev            lc = (ngx_stream_limit_conn_node_t *) &node->color;
196e18a033bSKonstantin Ananyev
197e18a033bSKonstantin Ananyev            if ((ngx_uint_t) lc->conn >= limits[i].conn) {
198e18a033bSKonstantin Ananyev
199e18a033bSKonstantin Ananyev                ngx_shmtx_unlock(&shpool->mutex);
200e18a033bSKonstantin Ananyev
201e18a033bSKonstantin Ananyev                ngx_log_error(lccf->log_level, s->connection->log, 0,
202e18a033bSKonstantin Ananyev                              "limiting connections by zone \"%V\"",
203e18a033bSKonstantin Ananyev                              &limits[i].shm_zone->shm.name);
204e18a033bSKonstantin Ananyev
205e18a033bSKonstantin Ananyev                ngx_stream_limit_conn_cleanup_all(s->connection->pool);
206e18a033bSKonstantin Ananyev                return NGX_STREAM_SERVICE_UNAVAILABLE;
207e18a033bSKonstantin Ananyev            }
208e18a033bSKonstantin Ananyev
209e18a033bSKonstantin Ananyev            lc->conn++;
210e18a033bSKonstantin Ananyev        }
211e18a033bSKonstantin Ananyev
212e18a033bSKonstantin Ananyev        ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
213e18a033bSKonstantin Ananyev                       "limit conn: %08Xi %d", node->key, lc->conn);
214e18a033bSKonstantin Ananyev
215e18a033bSKonstantin Ananyev        ngx_shmtx_unlock(&shpool->mutex);
216e18a033bSKonstantin Ananyev
217e18a033bSKonstantin Ananyev        cln = ngx_pool_cleanup_add(s->connection->pool,
218e18a033bSKonstantin Ananyev                                   sizeof(ngx_stream_limit_conn_cleanup_t));
219e18a033bSKonstantin Ananyev        if (cln == NULL) {
220e18a033bSKonstantin Ananyev            return NGX_ERROR;
221e18a033bSKonstantin Ananyev        }
222e18a033bSKonstantin Ananyev
223e18a033bSKonstantin Ananyev        cln->handler = ngx_stream_limit_conn_cleanup;
224e18a033bSKonstantin Ananyev        lccln = cln->data;
225e18a033bSKonstantin Ananyev
226e18a033bSKonstantin Ananyev        lccln->shm_zone = limits[i].shm_zone;
227e18a033bSKonstantin Ananyev        lccln->node = node;
228e18a033bSKonstantin Ananyev    }
229e18a033bSKonstantin Ananyev
230e18a033bSKonstantin Ananyev    return NGX_DECLINED;
231e18a033bSKonstantin Ananyev}
232e18a033bSKonstantin Ananyev
233e18a033bSKonstantin Ananyev
234e18a033bSKonstantin Ananyevstatic void
235e18a033bSKonstantin Ananyevngx_stream_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp,
236e18a033bSKonstantin Ananyev    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
237e18a033bSKonstantin Ananyev{
238e18a033bSKonstantin Ananyev    ngx_rbtree_node_t             **p;
239e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_node_t   *lcn, *lcnt;
240e18a033bSKonstantin Ananyev
241e18a033bSKonstantin Ananyev    for ( ;; ) {
242e18a033bSKonstantin Ananyev
243e18a033bSKonstantin Ananyev        if (node->key < temp->key) {
244e18a033bSKonstantin Ananyev
245e18a033bSKonstantin Ananyev            p = &temp->left;
246e18a033bSKonstantin Ananyev
247e18a033bSKonstantin Ananyev        } else if (node->key > temp->key) {
248e18a033bSKonstantin Ananyev
249e18a033bSKonstantin Ananyev            p = &temp->right;
250e18a033bSKonstantin Ananyev
251e18a033bSKonstantin Ananyev        } else { /* node->key == temp->key */
252e18a033bSKonstantin Ananyev
253e18a033bSKonstantin Ananyev            lcn = (ngx_stream_limit_conn_node_t *) &node->color;
254e18a033bSKonstantin Ananyev            lcnt = (ngx_stream_limit_conn_node_t *) &temp->color;
255e18a033bSKonstantin Ananyev
256e18a033bSKonstantin Ananyev            p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)
257e18a033bSKonstantin Ananyev                ? &temp->left : &temp->right;
258e18a033bSKonstantin Ananyev        }
259e18a033bSKonstantin Ananyev
260e18a033bSKonstantin Ananyev        if (*p == sentinel) {
261e18a033bSKonstantin Ananyev            break;
262e18a033bSKonstantin Ananyev        }
263e18a033bSKonstantin Ananyev
264e18a033bSKonstantin Ananyev        temp = *p;
265e18a033bSKonstantin Ananyev    }
266e18a033bSKonstantin Ananyev
267e18a033bSKonstantin Ananyev    *p = node;
268e18a033bSKonstantin Ananyev    node->parent = temp;
269e18a033bSKonstantin Ananyev    node->left = sentinel;
270e18a033bSKonstantin Ananyev    node->right = sentinel;
271e18a033bSKonstantin Ananyev    ngx_rbt_red(node);
272e18a033bSKonstantin Ananyev}
273e18a033bSKonstantin Ananyev
274e18a033bSKonstantin Ananyev
275e18a033bSKonstantin Ananyevstatic ngx_rbtree_node_t *
276e18a033bSKonstantin Ananyevngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key,
277e18a033bSKonstantin Ananyev    uint32_t hash)
278e18a033bSKonstantin Ananyev{
279e18a033bSKonstantin Ananyev    ngx_int_t                      rc;
280e18a033bSKonstantin Ananyev    ngx_rbtree_node_t             *node, *sentinel;
281e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_node_t  *lcn;
282e18a033bSKonstantin Ananyev
283e18a033bSKonstantin Ananyev    node = rbtree->root;
284e18a033bSKonstantin Ananyev    sentinel = rbtree->sentinel;
285e18a033bSKonstantin Ananyev
286e18a033bSKonstantin Ananyev    while (node != sentinel) {
287e18a033bSKonstantin Ananyev
288e18a033bSKonstantin Ananyev        if (hash < node->key) {
289e18a033bSKonstantin Ananyev            node = node->left;
290e18a033bSKonstantin Ananyev            continue;
291e18a033bSKonstantin Ananyev        }
292e18a033bSKonstantin Ananyev
293e18a033bSKonstantin Ananyev        if (hash > node->key) {
294e18a033bSKonstantin Ananyev            node = node->right;
295e18a033bSKonstantin Ananyev            continue;
296e18a033bSKonstantin Ananyev        }
297e18a033bSKonstantin Ananyev
298e18a033bSKonstantin Ananyev        /* hash == node->key */
299e18a033bSKonstantin Ananyev
300e18a033bSKonstantin Ananyev        lcn = (ngx_stream_limit_conn_node_t *) &node->color;
301e18a033bSKonstantin Ananyev
302e18a033bSKonstantin Ananyev        rc = ngx_memn2cmp(key->data, lcn->data, key->len, (size_t) lcn->len);
303e18a033bSKonstantin Ananyev
304e18a033bSKonstantin Ananyev        if (rc == 0) {
305e18a033bSKonstantin Ananyev            return node;
306e18a033bSKonstantin Ananyev        }
307e18a033bSKonstantin Ananyev
308e18a033bSKonstantin Ananyev        node = (rc < 0) ? node->left : node->right;
309e18a033bSKonstantin Ananyev    }
310e18a033bSKonstantin Ananyev
311e18a033bSKonstantin Ananyev    return NULL;
312e18a033bSKonstantin Ananyev}
313e18a033bSKonstantin Ananyev
314e18a033bSKonstantin Ananyev
315e18a033bSKonstantin Ananyevstatic void
316e18a033bSKonstantin Ananyevngx_stream_limit_conn_cleanup(void *data)
317e18a033bSKonstantin Ananyev{
318e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_cleanup_t  *lccln = data;
319e18a033bSKonstantin Ananyev
320e18a033bSKonstantin Ananyev    ngx_slab_pool_t               *shpool;
321e18a033bSKonstantin Ananyev    ngx_rbtree_node_t             *node;
322e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_ctx_t   *ctx;
323e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_node_t  *lc;
324e18a033bSKonstantin Ananyev
325e18a033bSKonstantin Ananyev    ctx = lccln->shm_zone->data;
326e18a033bSKonstantin Ananyev    shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr;
327e18a033bSKonstantin Ananyev    node = lccln->node;
328e18a033bSKonstantin Ananyev    lc = (ngx_stream_limit_conn_node_t *) &node->color;
329e18a033bSKonstantin Ananyev
330e18a033bSKonstantin Ananyev    ngx_shmtx_lock(&shpool->mutex);
331e18a033bSKonstantin Ananyev
332e18a033bSKonstantin Ananyev    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, lccln->shm_zone->shm.log, 0,
333e18a033bSKonstantin Ananyev                   "limit conn cleanup: %08Xi %d", node->key, lc->conn);
334e18a033bSKonstantin Ananyev
335e18a033bSKonstantin Ananyev    lc->conn--;
336e18a033bSKonstantin Ananyev
337e18a033bSKonstantin Ananyev    if (lc->conn == 0) {
338e18a033bSKonstantin Ananyev        ngx_rbtree_delete(ctx->rbtree, node);
339e18a033bSKonstantin Ananyev        ngx_slab_free_locked(shpool, node);
340e18a033bSKonstantin Ananyev    }
341e18a033bSKonstantin Ananyev
342e18a033bSKonstantin Ananyev    ngx_shmtx_unlock(&shpool->mutex);
343e18a033bSKonstantin Ananyev}
344e18a033bSKonstantin Ananyev
345e18a033bSKonstantin Ananyev
346e18a033bSKonstantin Ananyevstatic ngx_inline void
347e18a033bSKonstantin Ananyevngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool)
348e18a033bSKonstantin Ananyev{
349e18a033bSKonstantin Ananyev    ngx_pool_cleanup_t  *cln;
350e18a033bSKonstantin Ananyev
351e18a033bSKonstantin Ananyev    cln = pool->cleanup;
352e18a033bSKonstantin Ananyev
353e18a033bSKonstantin Ananyev    while (cln && cln->handler == ngx_stream_limit_conn_cleanup) {
354e18a033bSKonstantin Ananyev        ngx_stream_limit_conn_cleanup(cln->data);
355e18a033bSKonstantin Ananyev        cln = cln->next;
356e18a033bSKonstantin Ananyev    }
357e18a033bSKonstantin Ananyev
358e18a033bSKonstantin Ananyev    pool->cleanup = cln;
359e18a033bSKonstantin Ananyev}
360e18a033bSKonstantin Ananyev
361e18a033bSKonstantin Ananyev
362e18a033bSKonstantin Ananyevstatic ngx_int_t
363e18a033bSKonstantin Ananyevngx_stream_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
364e18a033bSKonstantin Ananyev{
365e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_ctx_t  *octx = data;
366e18a033bSKonstantin Ananyev
367e18a033bSKonstantin Ananyev    size_t                        len;
368e18a033bSKonstantin Ananyev    ngx_slab_pool_t              *shpool;
369e18a033bSKonstantin Ananyev    ngx_rbtree_node_t            *sentinel;
370e18a033bSKonstantin Ananyev    ngx_stream_limit_conn_ctx_t  *ctx;
371e18a033bSKonstantin Ananyev
372e18a033bSKonstantin Ananyev    ctx = shm_zone->data;
373e18a033bSKonstantin Ananyev
374e18a033bSKonstantin Ananyev    if (octx) {
375e18a033bSKonstantin Ananyev        if (ctx->key.value.len != octx->key.value.len
376e18a033bSKonstantin Ananyev            || ngx_strncmp(ctx->key.value.data, octx->key.value.data,
377e18a033bSKonstantin Ananyev                           ctx->key.value.len)
378e18a033bSKonstantin Ananyev               != 0)
379e18a033bSKonstantin Ananyev        {
380e18a033bSKonstantin Ananyev            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
381e18a033bSKonstantin Ananyev                          "limit_conn_zone \"%V\" uses the \"%V\" key "
382e18a033bSKonstantin Ananyev                          "while previously it used the \"%V\" key",
383e18a033bSKonstantin Ananyev                          &shm_zone->shm.name, &ctx->key.value,
384e18a033bSKonstantin Ananyev                          &octx->key.value);
385e18a033bSKonstantin Ananyev            return NGX_ERROR;
386e18a033bSKonstantin Ananyev        }
387e18a033bSKonstantin Ananyev
388e18a033bSKonstantin Ananyev        ctx->rbtree = octx->rbtree;
389e18a033bSKonstantin Ananyev
390e18a033bSKonstantin Ananyev        return NGX_OK;
391e18a033bSKonstantin Ananyev    }
392e18a033bSKonstantin Ananyev
393e18a033bSKonstantin Ananyev    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
394e18a033bSKonstantin Ananyev
395e18a033bSKonstantin Ananyev    if (shm_zone->shm.exists) {
396e18a033bSKonstantin Ananyev        ctx->rbtree = shpool->data;
397e18a033bSKonstantin Ananyev
398e18a033bSKonstantin Ananyev        return NGX_OK;
399e18a033bSKonstantin Ananyev    }
400e18a033bSKonstantin Ananyev
401e18a033bSKonstantin Ananyev    ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));
402e18a033bSKonstantin Ananyev    if (ctx->rbtree == NULL) {
403e18a033bSKonstantin Ananyev        return NGX_ERROR;
404e18a033bSKonstantin Ananyev    }
405e18a033bSKonstantin Ananyev
406e18a033bSKonstantin Ananyev    shpool->data = ctx->rbtree;
407e18a033bSKonstantin Ananyev
408e18a033bSKonstantin Ananyev    sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));
409e18a033bSKonstantin Ananyev    if (sentinel == NULL) {
410e18a033bSKonstantin Ananyev        return NGX_ERROR;
411e18a033bSKonstantin Ananyev    }
412e18a033bSKonstantin Ananyev
413e18a033bSKonstantin Ananyev    ngx_rbtree_init(ctx->rbtree, sentinel,
414e18a033bSKonstantin Ananyev<