Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 typedef struct {
0014     u_char                     color;
0015     u_char                     len;
0016     u_short                    conn;
0017     u_char                     data[1];
0018 } ngx_http_limit_conn_node_t;
0019 
0020 
0021 typedef struct {
0022     ngx_shm_zone_t            *shm_zone;
0023     ngx_rbtree_node_t         *node;
0024 } ngx_http_limit_conn_cleanup_t;
0025 
0026 
0027 typedef struct {
0028     ngx_rbtree_t              *rbtree;
0029     ngx_http_complex_value_t   key;
0030 } ngx_http_limit_conn_ctx_t;
0031 
0032 
0033 typedef struct {
0034     ngx_shm_zone_t            *shm_zone;
0035     ngx_uint_t                 conn;
0036 } ngx_http_limit_conn_limit_t;
0037 
0038 
0039 typedef struct {
0040     ngx_array_t                limits;
0041     ngx_uint_t                 log_level;
0042     ngx_uint_t                 status_code;
0043 } ngx_http_limit_conn_conf_t;
0044 
0045 
0046 static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree,
0047     ngx_str_t *key, uint32_t hash);
0048 static void ngx_http_limit_conn_cleanup(void *data);
0049 static ngx_inline void ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool);
0050 
0051 static void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf);
0052 static char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
0053     void *child);
0054 static char *ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
0055     void *conf);
0056 static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
0057     void *conf);
0058 static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf);
0059 
0060 
0061 static ngx_conf_enum_t  ngx_http_limit_conn_log_levels[] = {
0062     { ngx_string("info"), NGX_LOG_INFO },
0063     { ngx_string("notice"), NGX_LOG_NOTICE },
0064     { ngx_string("warn"), NGX_LOG_WARN },
0065     { ngx_string("error"), NGX_LOG_ERR },
0066     { ngx_null_string, 0 }
0067 };
0068 
0069 
0070 static ngx_conf_num_bounds_t  ngx_http_limit_conn_status_bounds = {
0071     ngx_conf_check_num_bounds, 400, 599
0072 };
0073 
0074 
0075 static ngx_command_t  ngx_http_limit_conn_commands[] = {
0076 
0077     { ngx_string("limit_conn_zone"),
0078       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,
0079       ngx_http_limit_conn_zone,
0080       0,
0081       0,
0082       NULL },
0083 
0084     { ngx_string("limit_conn"),
0085       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
0086       ngx_http_limit_conn,
0087       NGX_HTTP_LOC_CONF_OFFSET,
0088       0,
0089       NULL },
0090 
0091     { ngx_string("limit_conn_log_level"),
0092       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0093       ngx_conf_set_enum_slot,
0094       NGX_HTTP_LOC_CONF_OFFSET,
0095       offsetof(ngx_http_limit_conn_conf_t, log_level),
0096       &ngx_http_limit_conn_log_levels },
0097 
0098     { ngx_string("limit_conn_status"),
0099       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0100       ngx_conf_set_num_slot,
0101       NGX_HTTP_LOC_CONF_OFFSET,
0102       offsetof(ngx_http_limit_conn_conf_t, status_code),
0103       &ngx_http_limit_conn_status_bounds },
0104 
0105       ngx_null_command
0106 };
0107 
0108 
0109 static ngx_http_module_t  ngx_http_limit_conn_module_ctx = {
0110     NULL,                                  /* preconfiguration */
0111     ngx_http_limit_conn_init,              /* postconfiguration */
0112 
0113     NULL,                                  /* create main configuration */
0114     NULL,                                  /* init main configuration */
0115 
0116     NULL,                                  /* create server configuration */
0117     NULL,                                  /* merge server configuration */
0118 
0119     ngx_http_limit_conn_create_conf,       /* create location configuration */
0120     ngx_http_limit_conn_merge_conf         /* merge location configuration */
0121 };
0122 
0123 
0124 ngx_module_t  ngx_http_limit_conn_module = {
0125     NGX_MODULE_V1,
0126     &ngx_http_limit_conn_module_ctx,       /* module context */
0127     ngx_http_limit_conn_commands,          /* module directives */
0128     NGX_HTTP_MODULE,                       /* module type */
0129     NULL,                                  /* init master */
0130     NULL,                                  /* init module */
0131     NULL,                                  /* init process */
0132     NULL,                                  /* init thread */
0133     NULL,                                  /* exit thread */
0134     NULL,                                  /* exit process */
0135     NULL,                                  /* exit master */
0136     NGX_MODULE_V1_PADDING
0137 };
0138 
0139 
0140 static ngx_int_t
0141 ngx_http_limit_conn_handler(ngx_http_request_t *r)
0142 {
0143     size_t                          n;
0144     uint32_t                        hash;
0145     ngx_str_t                       key;
0146     ngx_uint_t                      i;
0147     ngx_slab_pool_t                *shpool;
0148     ngx_rbtree_node_t              *node;
0149     ngx_pool_cleanup_t             *cln;
0150     ngx_http_limit_conn_ctx_t      *ctx;
0151     ngx_http_limit_conn_node_t     *lc;
0152     ngx_http_limit_conn_conf_t     *lccf;
0153     ngx_http_limit_conn_limit_t    *limits;
0154     ngx_http_limit_conn_cleanup_t  *lccln;
0155 
0156     if (r->main->limit_conn_set) {
0157         return NGX_DECLINED;
0158     }
0159 
0160     lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module);
0161     limits = lccf->limits.elts;
0162 
0163     for (i = 0; i < lccf->limits.nelts; i++) {
0164         ctx = limits[i].shm_zone->data;
0165 
0166         if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
0167             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0168         }
0169 
0170         if (key.len == 0) {
0171             continue;
0172         }
0173 
0174         if (key.len > 255) {
0175             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0176                           "the value of the \"%V\" key "
0177                           "is more than 255 bytes: \"%V\"",
0178                           &ctx->key.value, &key);
0179             continue;
0180         }
0181 
0182         r->main->limit_conn_set = 1;
0183 
0184         hash = ngx_crc32_short(key.data, key.len);
0185 
0186         shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr;
0187 
0188         ngx_shmtx_lock(&shpool->mutex);
0189 
0190         node = ngx_http_limit_conn_lookup(ctx->rbtree, &key, hash);
0191 
0192         if (node == NULL) {
0193 
0194             n = offsetof(ngx_rbtree_node_t, color)
0195                 + offsetof(ngx_http_limit_conn_node_t, data)
0196                 + key.len;
0197 
0198             node = ngx_slab_alloc_locked(shpool, n);
0199 
0200             if (node == NULL) {
0201                 ngx_shmtx_unlock(&shpool->mutex);
0202                 ngx_http_limit_conn_cleanup_all(r->pool);
0203                 return lccf->status_code;
0204             }
0205 
0206             lc = (ngx_http_limit_conn_node_t *) &node->color;
0207 
0208             node->key = hash;
0209             lc->len = (u_char) key.len;
0210             lc->conn = 1;
0211             ngx_memcpy(lc->data, key.data, key.len);
0212 
0213             ngx_rbtree_insert(ctx->rbtree, node);
0214 
0215         } else {
0216 
0217             lc = (ngx_http_limit_conn_node_t *) &node->color;
0218 
0219             if ((ngx_uint_t) lc->conn >= limits[i].conn) {
0220 
0221                 ngx_shmtx_unlock(&shpool->mutex);
0222 
0223                 ngx_log_error(lccf->log_level, r->connection->log, 0,
0224                               "limiting connections by zone \"%V\"",
0225                               &limits[i].shm_zone->shm.name);
0226 
0227                 ngx_http_limit_conn_cleanup_all(r->pool);
0228                 return lccf->status_code;
0229             }
0230 
0231             lc->conn++;
0232         }
0233 
0234         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0235                        "limit conn: %08Xi %d", node->key, lc->conn);
0236 
0237         ngx_shmtx_unlock(&shpool->mutex);
0238 
0239         cln = ngx_pool_cleanup_add(r->pool,
0240                                    sizeof(ngx_http_limit_conn_cleanup_t));
0241         if (cln == NULL) {
0242             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0243         }
0244 
0245         cln->handler = ngx_http_limit_conn_cleanup;
0246         lccln = cln->data;
0247 
0248         lccln->shm_zone = limits[i].shm_zone;
0249         lccln->node = node;
0250     }
0251 
0252     return NGX_DECLINED;
0253 }
0254 
0255 
0256 static void
0257 ngx_http_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp,
0258     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
0259 {
0260     ngx_rbtree_node_t           **p;
0261     ngx_http_limit_conn_node_t   *lcn, *lcnt;
0262 
0263     for ( ;; ) {
0264 
0265         if (node->key < temp->key) {
0266 
0267             p = &temp->left;
0268 
0269         } else if (node->key > temp->key) {
0270 
0271             p = &temp->right;
0272 
0273         } else { /* node->key == temp->key */
0274 
0275             lcn = (ngx_http_limit_conn_node_t *) &node->color;
0276             lcnt = (ngx_http_limit_conn_node_t *) &temp->color;
0277 
0278             p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)
0279                 ? &temp->left : &temp->right;
0280         }
0281 
0282         if (*p == sentinel) {
0283             break;
0284         }
0285 
0286         temp = *p;
0287     }
0288 
0289     *p = node;
0290     node->parent = temp;
0291     node->left = sentinel;
0292     node->right = sentinel;
0293     ngx_rbt_red(node);
0294 }
0295 
0296 
0297 static ngx_rbtree_node_t *
0298 ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key, uint32_t hash)
0299 {
0300     ngx_int_t                    rc;
0301     ngx_rbtree_node_t           *node, *sentinel;
0302     ngx_http_limit_conn_node_t  *lcn;
0303 
0304     node = rbtree->root;
0305     sentinel = rbtree->sentinel;
0306 
0307     while (node != sentinel) {
0308 
0309         if (hash < node->key) {
0310             node = node->left;
0311             continue;
0312         }
0313 
0314         if (hash > node->key) {
0315             node = node->right;
0316             continue;
0317         }
0318 
0319         /* hash == node->key */
0320 
0321         lcn = (ngx_http_limit_conn_node_t *) &node->color;
0322 
0323         rc = ngx_memn2cmp(key->data, lcn->data, key->len, (size_t) lcn->len);
0324 
0325         if (rc == 0) {
0326             return node;
0327         }
0328 
0329         node = (rc < 0) ? node->left : node->right;
0330     }
0331 
0332     return NULL;
0333 }
0334 
0335 
0336 static void
0337 ngx_http_limit_conn_cleanup(void *data)
0338 {
0339     ngx_http_limit_conn_cleanup_t  *lccln = data;
0340 
0341     ngx_slab_pool_t             *shpool;
0342     ngx_rbtree_node_t           *node;
0343     ngx_http_limit_conn_ctx_t   *ctx;
0344     ngx_http_limit_conn_node_t  *lc;
0345 
0346     ctx = lccln->shm_zone->data;
0347     shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr;
0348     node = lccln->node;
0349     lc = (ngx_http_limit_conn_node_t *) &node->color;
0350 
0351     ngx_shmtx_lock(&shpool->mutex);
0352 
0353     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0,
0354                    "limit conn cleanup: %08Xi %d", node->key, lc->conn);
0355 
0356     lc->conn--;
0357 
0358     if (lc->conn == 0) {
0359         ngx_rbtree_delete(ctx->rbtree, node);
0360         ngx_slab_free_locked(shpool, node);
0361     }
0362 
0363     ngx_shmtx_unlock(&shpool->mutex);
0364 }
0365 
0366 
0367 static ngx_inline void
0368 ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool)
0369 {
0370     ngx_pool_cleanup_t  *cln;
0371 
0372     cln = pool->cleanup;
0373 
0374     while (cln && cln->handler == ngx_http_limit_conn_cleanup) {
0375         ngx_http_limit_conn_cleanup(cln->data);
0376         cln = cln->next;
0377     }
0378 
0379     pool->cleanup = cln;
0380 }
0381 
0382 
0383 static ngx_int_t
0384 ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
0385 {
0386     ngx_http_limit_conn_ctx_t  *octx = data;
0387 
0388     size_t                      len;
0389     ngx_slab_pool_t            *shpool;
0390     ngx_rbtree_node_t          *sentinel;
0391     ngx_http_limit_conn_ctx_t  *ctx;
0392 
0393     ctx = shm_zone->data;
0394 
0395     if (octx) {
0396         if (ctx->key.value.len != octx->key.value.len
0397             || ngx_strncmp(ctx->key.value.data, octx->key.value.data,
0398                            ctx->key.value.len)
0399                != 0)
0400         {
0401             ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
0402                           "limit_conn_zone \"%V\" uses the \"%V\" key "
0403                           "while previously it used the \"%V\" key",
0404                           &shm_zone->shm.name, &ctx->key.value,
0405                           &octx->key.value);
0406             return NGX_ERROR;
0407         }
0408 
0409         ctx->rbtree = octx->rbtree;
0410 
0411         return NGX_OK;
0412     }
0413 
0414     shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
0415 
0416     if (shm_zone->shm.exists) {
0417         ctx->rbtree = shpool->data;
0418 
0419         return NGX_OK;
0420     }
0421 
0422     ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));
0423     if (ctx->rbtree == NULL) {
0424         return NGX_ERROR;
0425     }
0426 
0427     shpool->data = ctx->rbtree;
0428 
0429     sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));
0430     if (sentinel == NULL) {
0431         return NGX_ERROR;
0432     }
0433 
0434     ngx_rbtree_init(ctx->rbtree, sentinel,
0435                     ngx_http_limit_conn_rbtree_insert_value);
0436 
0437     len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len;
0438 
0439     shpool->log_ctx = ngx_slab_alloc(shpool, len);
0440     if (shpool->log_ctx == NULL) {
0441         return NGX_ERROR;
0442     }
0443 
0444     ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z",
0445                 &shm_zone->shm.name);
0446 
0447     return NGX_OK;
0448 }
0449 
0450 
0451 static void *
0452 ngx_http_limit_conn_create_conf(ngx_conf_t *cf)
0453 {
0454     ngx_http_limit_conn_conf_t  *conf;
0455 
0456     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_conf_t));
0457     if (conf == NULL) {
0458         return NULL;
0459     }
0460 
0461     /*
0462      * set by ngx_pcalloc():
0463      *
0464      *     conf->limits.elts = NULL;
0465      */
0466 
0467     conf->log_level = NGX_CONF_UNSET_UINT;
0468     conf->status_code = NGX_CONF_UNSET_UINT;
0469 
0470     return conf;
0471 }
0472 
0473 
0474 static char *
0475 ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)
0476 {
0477     ngx_http_limit_conn_conf_t *prev = parent;
0478     ngx_http_limit_conn_conf_t *conf = child;
0479 
0480     if (conf->limits.elts == NULL) {
0481         conf->limits = prev->limits;
0482     }
0483 
0484     ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);
0485     ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
0486                               NGX_HTTP_SERVICE_UNAVAILABLE);
0487 
0488     return NGX_CONF_OK;
0489 }
0490 
0491 
0492 static char *
0493 ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0494 {
0495     u_char                            *p;
0496     ssize_t                            size;
0497     ngx_str_t                         *value, name, s;
0498     ngx_uint_t                         i;
0499     ngx_shm_zone_t                    *shm_zone;
0500     ngx_http_limit_conn_ctx_t         *ctx;
0501     ngx_http_compile_complex_value_t   ccv;
0502 
0503     value = cf->args->elts;
0504 
0505     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t));
0506     if (ctx == NULL) {
0507         return NGX_CONF_ERROR;
0508     }
0509 
0510     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
0511 
0512     ccv.cf = cf;
0513     ccv.value = &value[1];
0514     ccv.complex_value = &ctx->key;
0515 
0516     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
0517         return NGX_CONF_ERROR;
0518     }
0519 
0520     size = 0;
0521     name.len = 0;
0522 
0523     for (i = 2; i < cf->args->nelts; i++) {
0524 
0525         if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
0526 
0527             name.data = value[i].data + 5;
0528 
0529             p = (u_char *) ngx_strchr(name.data, ':');
0530 
0531             if (p == NULL) {
0532                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0533                                    "invalid zone size \"%V\"", &value[i]);
0534                 return NGX_CONF_ERROR;
0535             }
0536 
0537             name.len = p - name.data;
0538 
0539             s.data = p + 1;
0540             s.len = value[i].data + value[i].len - s.data;
0541 
0542             size = ngx_parse_size(&s);
0543 
0544             if (size == NGX_ERROR) {
0545                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0546                                    "invalid zone size \"%V\"", &value[i]);
0547                 return NGX_CONF_ERROR;
0548             }
0549 
0550             if (size < (ssize_t) (8 * ngx_pagesize)) {
0551                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0552                                    "zone \"%V\" is too small", &value[i]);
0553                 return NGX_CONF_ERROR;
0554             }
0555 
0556             continue;
0557         }
0558 
0559         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0560                            "invalid parameter \"%V\"", &value[i]);
0561         return NGX_CONF_ERROR;
0562     }
0563 
0564     if (name.len == 0) {
0565         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0566                            "\"%V\" must have \"zone\" parameter",
0567                            &cmd->name);
0568         return NGX_CONF_ERROR;
0569     }
0570 
0571     shm_zone = ngx_shared_memory_add(cf, &name, size,
0572                                      &ngx_http_limit_conn_module);
0573     if (shm_zone == NULL) {
0574         return NGX_CONF_ERROR;
0575     }
0576 
0577     if (shm_zone->data) {
0578         ctx = shm_zone->data;
0579 
0580         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0581                            "%V \"%V\" is already bound to key \"%V\"",
0582                            &cmd->name, &name, &ctx->key.value);
0583         return NGX_CONF_ERROR;
0584     }
0585 
0586     shm_zone->init = ngx_http_limit_conn_init_zone;
0587     shm_zone->data = ctx;
0588 
0589     return NGX_CONF_OK;
0590 }
0591 
0592 
0593 static char *
0594 ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0595 {
0596     ngx_shm_zone_t               *shm_zone;
0597     ngx_http_limit_conn_conf_t   *lccf = conf;
0598     ngx_http_limit_conn_limit_t  *limit, *limits;
0599 
0600     ngx_str_t  *value;
0601     ngx_int_t   n;
0602     ngx_uint_t  i;
0603 
0604     value = cf->args->elts;
0605 
0606     shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
0607                                      &ngx_http_limit_conn_module);
0608     if (shm_zone == NULL) {
0609         return NGX_CONF_ERROR;
0610     }
0611 
0612     limits = lccf->limits.elts;
0613 
0614     if (limits == NULL) {
0615         if (ngx_array_init(&lccf->limits, cf->pool, 1,
0616                            sizeof(ngx_http_limit_conn_limit_t))
0617             != NGX_OK)
0618         {
0619             return NGX_CONF_ERROR;
0620         }
0621     }
0622 
0623     for (i = 0; i < lccf->limits.nelts; i++) {
0624         if (shm_zone == limits[i].shm_zone) {
0625             return "is duplicate";
0626         }
0627     }
0628 
0629     n = ngx_atoi(value[2].data, value[2].len);
0630     if (n <= 0) {
0631         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0632                            "invalid number of connections \"%V\"", &value[2]);
0633         return NGX_CONF_ERROR;
0634     }
0635 
0636     if (n > 65535) {
0637         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0638                            "connection limit must be less 65536");
0639         return NGX_CONF_ERROR;
0640     }
0641 
0642     limit = ngx_array_push(&lccf->limits);
0643     if (limit == NULL) {
0644         return NGX_CONF_ERROR;
0645     }
0646 
0647     limit->conn = n;
0648     limit->shm_zone = shm_zone;
0649 
0650     return NGX_CONF_OK;
0651 }
0652 
0653 
0654 static ngx_int_t
0655 ngx_http_limit_conn_init(ngx_conf_t *cf)
0656 {
0657     ngx_http_handler_pt        *h;
0658     ngx_http_core_main_conf_t  *cmcf;
0659 
0660     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
0661 
0662     h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
0663     if (h == NULL) {
0664         return NGX_ERROR;
0665     }
0666 
0667     *h = ngx_http_limit_conn_handler;
0668 
0669     return NGX_OK;
0670 }