//garbage-collecting slab allocator
void * ngx_http_push_slab_alloc_locked(size_t size) {
	void  *p;
	if((p = ngx_slab_alloc_locked(ngx_http_push_shpool, size))==NULL) {
		ngx_http_push_channel_queue_t *ccur, *cnext;
		ngx_uint_t                  collected = 0;
		//failed. emergency garbage sweep, then.
		
		//collect channels
		ngx_queue_init(&channel_gc_sentinel.queue);
		ngx_http_push_walk_rbtree(ngx_http_push_channel_collector);
		for(ccur=(ngx_http_push_channel_queue_t *)ngx_queue_next(&channel_gc_sentinel.queue); ccur != &channel_gc_sentinel; ccur=cnext) {
			cnext = (ngx_http_push_channel_queue_t *)ngx_queue_next(&ccur->queue);
			ngx_http_push_delete_channel_locked(ccur->channel);
			ngx_free(ccur);
			collected++;
		}
		
		//todo: collect worker messages maybe
		
		ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, "push module: out of shared memory. emergency garbage collection deleted %ui unused channels.", collected);
		
		return ngx_slab_alloc_locked(ngx_http_push_shpool, size);
	}
	return p;
}
// find a channel by id. if channel not found, make one, insert it, and return that.
static ngx_http_push_stream_channel_t *
ngx_http_push_stream_get_channel(ngx_str_t *id, ngx_log_t *log, ngx_http_push_stream_loc_conf_t *cf)
{
    ngx_http_push_stream_main_conf_t      *mcf = ngx_http_push_stream_module_main_conf;
    ngx_http_push_stream_shm_data_t       *data = (ngx_http_push_stream_shm_data_t *) ngx_http_push_stream_shm_zone->data;
    ngx_http_push_stream_channel_t        *channel;
    ngx_slab_pool_t                       *shpool = (ngx_slab_pool_t *) ngx_http_push_stream_shm_zone->shm.addr;
    ngx_flag_t                             is_broadcast_channel = 0;

    channel = ngx_http_push_stream_find_channel(id, log);
    if (channel != NULL) { // we found our channel
        return channel;
    }

    ngx_shmtx_lock(&shpool->mutex);

    // check again to see if any other worker didn't create the channel
    channel = ngx_http_push_stream_find_channel(id, log);
    if (channel != NULL) { // we found our channel
        ngx_shmtx_unlock(&shpool->mutex);
        return channel;
    }

    if ((mcf->broadcast_channel_prefix.len > 0) && (ngx_strncmp(id->data, mcf->broadcast_channel_prefix.data, mcf->broadcast_channel_prefix.len) == 0)) {
        is_broadcast_channel = 1;
    }

    if (((!is_broadcast_channel) && (mcf->max_number_of_channels != NGX_CONF_UNSET_UINT) && (mcf->max_number_of_channels == data->channels)) ||
        ((is_broadcast_channel) && (mcf->max_number_of_broadcast_channels != NGX_CONF_UNSET_UINT) && (mcf->max_number_of_broadcast_channels == data->broadcast_channels))) {
        ngx_shmtx_unlock(&shpool->mutex);
        return NGX_HTTP_PUSH_STREAM_NUMBER_OF_CHANNELS_EXCEEDED;
    }

    if ((channel = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_push_stream_channel_t))) == NULL) {
        ngx_shmtx_unlock(&shpool->mutex);
        return NULL;
    }

    if ((channel->id.data = ngx_slab_alloc_locked(shpool, id->len + 1)) == NULL) {
        ngx_slab_free_locked(shpool, channel);
        ngx_shmtx_unlock(&shpool->mutex);
        return NULL;
    }

    channel->id.len = id->len;
    ngx_memcpy(channel->id.data, id->data, channel->id.len);
    channel->id.data[channel->id.len] = '\0';

    channel->broadcast = is_broadcast_channel;

    ngx_http_push_stream_initialize_channel(channel);

    // initialize workers_with_subscribers queues only when a channel is created
    ngx_queue_init(&channel->workers_with_subscribers);

    ngx_shmtx_unlock(&shpool->mutex);
    return channel;
}
static ngx_http_push_stream_pid_queue_t *
ngx_http_push_stream_create_worker_subscriber_channel_sentinel_locked(ngx_slab_pool_t *shpool, ngx_str_t *channel_id, ngx_log_t *log)
{
    ngx_http_push_stream_pid_queue_t     *worker_sentinel;
    ngx_http_push_stream_channel_t       *channel;

    // check if channel still exists
    if ((channel = ngx_http_push_stream_find_channel(channel_id, log)) == NULL) {
        ngx_log_error(NGX_LOG_ERR, log, 0, "push stream module: something goes very wrong, arrived on ngx_http_push_stream_subscriber_assign_channel without created channel %s", channel_id->data);
        return NULL;
    }

    if ((worker_sentinel = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_push_stream_pid_queue_t))) == NULL) {
        ngx_log_error(NGX_LOG_ERR, log, 0, "push stream module: unable to allocate worker subscriber queue marker in shared memory");
        return NULL;
    }

    // initialize
    ngx_queue_insert_tail(&channel->workers_with_subscribers, &worker_sentinel->queue);

    worker_sentinel->pid = ngx_pid;
    worker_sentinel->slot = ngx_process_slot;
    ngx_queue_init(&worker_sentinel->subscribers_sentinel.queue);

    return worker_sentinel;
}
static ngx_btt_peer_t *
ngx_btt_get_peer(ngx_btt_conf_t *bcf, ngx_btt_ctx_t *ctx, ngx_rbtree_t *tree,
    ngx_queue_t *queue)
{
    ngx_queue_t     *q;
    ngx_btt_peer_t  *p;

    p = NULL;

    if (!ngx_queue_empty(&bcf->btt->free_peers)) {
        q = ngx_queue_head(&bcf->btt->free_peers);
        ngx_queue_remove(q);

        p = ngx_queue_data(q, ngx_btt_peer_t, queue);
    }

    if (p == NULL) {
        p = ngx_slab_alloc_locked(bcf->pool, sizeof(ngx_btt_peer_t));
        if (p == NULL) {
            return NULL;
        }
    }

    /* TODO */

    ngx_memcpy(&p->connection_id, &ctx->connection_id,
               sizeof(ctx->connection_id));

    p->node.key = ngx_crc32_short((u_char *) &ctx->connection_id,
                                  sizeof(ctx->connection_id));
    ngx_rbtree_insert(tree, &p->node);
    ngx_queue_insert_head(queue, &p->queue);

    return p;
}
static ngx_http_cloudrouter_remote_t*
ngx_http_cloudrouter_add_remote_locked(ngx_http_cloudrouter_node_t* e) {
    ngx_http_cloudrouter_remote_t *remote = NULL;
    if (e->remote == NULL) {
        e->remote = ngx_slab_alloc_locked(ngx_http_cloudrouter_shpool, sizeof(ngx_http_cloudrouter_remote_t));
        remote = e->remote;
    } else {
        remote = e->remote;
        while (remote->next != NULL) {
            remote = remote->next;
        }
        remote->next = ngx_slab_alloc_locked(ngx_http_cloudrouter_shpool, sizeof(ngx_http_cloudrouter_remote_t));
        remote = remote->next;
    }
    ngx_memzero(remote, sizeof(ngx_http_cloudrouter_remote_t));
    return remote;
}
void *ngx_status_get_momerybyhost_brtree(ngx_http_request_t *r)
{
	ssize_t						n,len;
	uint32_t					hash;
	ngx_tcp_io					*io_ret;
	ngx_slab_pool_t     		*shpool;
	ngx_rbtree_node_t			*node, *o_node;
	ngx_status_ex_host_ctx_t	ctx;
	
	if (r == NULL || r->headers_in.host == NULL)
		return NULL;

	if (status_ex_shm_zone == NULL || status_ex_shm_zone->data == NULL)
		return NULL;

	if (r->headers_in.host->value.len > URL_LEN)
		return NULL;
	
	ctx.rbtree = *(ngx_rbtree_t **)status_ex_shm_zone->data;
	
	hash = ngx_crc32_short(r->headers_in.host->value.data, r->headers_in.host->value.len);
	node = ngx_host_list_lookup(ctx.rbtree, &r->headers_in.host->value, hash);

	/*==NULL insert new node*/
	if (node == NULL)
	{
		len = r->headers_in.host->value.len;
		shpool = (ngx_slab_pool_t *)status_ex_shm_zone->shm.addr;
		n = offsetof(ngx_rbtree_node_t, color)
			+ offsetof(ngx_tcp_io, host)
			+ len;

		node = ngx_slab_alloc_locked(shpool, n);
		if (node == NULL) {
	   		return NULL;
	    }

	    io_ret = (ngx_tcp_io *) &node->data;

	    node->key = hash;
	    io_ret->host_len= len;
	    ngx_memcpy(io_ret->host, r->headers_in.host->value.data, r->headers_in.host->value.len);

		ngx_shmtx_lock(&shpool->mutex);
		/*secend find*/
		o_node = ngx_host_list_lookup(ctx.rbtree, &r->headers_in.host->value, hash);
		if (o_node == NULL)
		{
			ngx_rbtree_insert(ctx.rbtree, node);
			statu_url_io_array->ngx_tcp_io[statu_url_io_array->number] = io_ret;
			statu_url_io_array->number++;
		}
		
		ngx_shmtx_unlock(&shpool->mutex);
	}
	
	return (ngx_tcp_io *) &node->data;
}
static ngx_int_t
ngx_http_vhost_traffic_status_dump_restore_add_node(ngx_event_t *ev,
     ngx_http_vhost_traffic_status_node_t *ovtsn, ngx_str_t *key)
{
    size_t                                 size;
    uint32_t                               hash;
    ngx_slab_pool_t                       *shpool;
    ngx_rbtree_node_t                     *node;
    ngx_http_vhost_traffic_status_ctx_t   *ctx;
    ngx_http_vhost_traffic_status_node_t  *vtsn;

    ctx = ev->data;

    if (key->len == 0) {
        return NGX_ERROR;
    }

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

    ngx_shmtx_lock(&shpool->mutex);

    /* find node */
    hash = ngx_crc32_short(key->data, key->len);

    node = ngx_http_vhost_traffic_status_node_lookup(ctx->rbtree, key, hash);

    /* copy node */
    if (node == NULL) {
        size = offsetof(ngx_rbtree_node_t, color)
               + offsetof(ngx_http_vhost_traffic_status_node_t, data)
               + key->len;

        node = ngx_slab_alloc_locked(shpool, size);
        if (node == NULL) {
            ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
                          "dump_restore_add_node::ngx_slab_alloc_locked() failed");

            ngx_shmtx_unlock(&shpool->mutex);
            return NGX_ERROR;
        }

        vtsn = (ngx_http_vhost_traffic_status_node_t *) &node->color;

        node->key = hash;

        *vtsn = *ovtsn;

        ngx_memcpy(vtsn->data, key->data, key->len);

        ngx_rbtree_insert(ctx->rbtree, node);
    }

    ngx_shmtx_unlock(&shpool->mutex);

    return NGX_OK;
}
/*
 *	添加缓存文件到红黑树和队列中,通过 ngx_http_file_cache_t 结构管理此节点
 */
static ngx_int_t
ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
{
    ngx_http_file_cache_node_t  *fcn;

    ngx_shmtx_lock(&cache->shpool->mutex);

	//	根据key在二叉树中查找,没有找到添加一个节点到cache中
    fcn = ngx_http_file_cache_lookup(cache, c->key);

    if (fcn == NULL) {

        fcn = ngx_slab_alloc_locked(cache->shpool,
                                    sizeof(ngx_http_file_cache_node_t));
        if (fcn == NULL) {
            ngx_shmtx_unlock(&cache->shpool->mutex);
            return NGX_ERROR;
        }

		//	设置node结点key
        ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));

        ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
                   NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));

		//	当前结点插入到二叉树中
        ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);

        fcn->uses = 1;
        fcn->count = 0;
        fcn->valid_msec = 0;
        fcn->error = 0;
        fcn->exists = 1;
        fcn->updating = 0;
        fcn->deleting = 0;
        fcn->uniq = 0;
        fcn->valid_sec = 0;
        fcn->body_start = 0;
        fcn->fs_size = c->fs_size;

        cache->sh->size += c->fs_size;

    } else {
        ngx_queue_remove(&fcn->queue);
    }

	//	设置过期时间(当前时间+不活跃时间)
    fcn->expire = ngx_time() + cache->inactive;

	//	插入到队列头部,先插入的在队列的尾部
    ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

    ngx_shmtx_unlock(&cache->shpool->mutex);

    return NGX_OK;
}
示例#9
0
void * ngx_http_session_shm_alloc_nolock(size_t size)
{
    void                             *p;
    ngx_http_session_list_t          *session_list;

    session_list = ngx_http_session_shm_zone->data;

    p = ngx_slab_alloc_locked(session_list->shpool, size);

    return p;
}
示例#10
0
void *
ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size)
{
    void  *p;

    ngx_shmtx_lock(&pool->mutex);

    p = ngx_slab_alloc_locked(pool, size);

    ngx_shmtx_unlock(&pool->mutex);

    return p;
}
示例#11
0
static ngx_int_t
ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
{
    ngx_http_file_cache_node_t  *fcn;

    ngx_shmtx_lock(&cache->shpool->mutex);

    fcn = ngx_http_file_cache_lookup(cache, c->key);

    if (fcn == NULL) {

        fcn = ngx_slab_alloc_locked(cache->shpool,
                                    sizeof(ngx_http_file_cache_node_t));
        if (fcn == NULL) {
            ngx_shmtx_unlock(&cache->shpool->mutex);
            return NGX_ERROR;
        }

        ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));

        ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
                   NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));

        ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);

        fcn->uses = 1;
        fcn->count = 0;
        fcn->valid_msec = c->valid_msec;
        fcn->error = 0;
        fcn->exists = 1;
        fcn->updating = 0;
        fcn->deleting = 0;
        fcn->uniq = c->uniq;
        fcn->valid_sec = c->valid_sec;
        fcn->body_start = c->body_start;
        fcn->fs_size = c->fs_size;

        cache->sh->size += c->fs_size;

    } else {
        ngx_queue_remove(&fcn->queue);
    }

    fcn->expire = ngx_time() + cache->inactive;

    ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

    ngx_shmtx_unlock(&cache->shpool->mutex);

    return NGX_OK;
}
示例#12
0
void * ngx_http_session_shm_alloc(size_t size)
{
    ngx_http_session_list_t          *session_list;
    void                             *p;

    session_list = ngx_http_session_shm_zone->data;

    ngx_shmtx_lock(&session_list->shpool->mutex);

    p = ngx_slab_alloc_locked(session_list->shpool, size);

    ngx_shmtx_unlock(&session_list->shpool->mutex);

    return p;
}
static ngx_int_t setup_default_servers(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us)
{
    ngx_http_upstream_available_capacity_srv_conf_t *conf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_available_capacity_module);
    if (conf->server_list) return NGX_OK;
    
    ngx_slab_pool_t *shpool = (ngx_slab_pool_t *)us->shm_zone->shm.addr;
    ngx_shmtx_lock(&shpool->mutex);

    ngx_http_upstream_available_capacity_server_t *prev_server_conf = NULL;
	size_t i = 0;
    for (i = 0; i < us->servers->nelts; ++i) {
        ngx_http_upstream_server_t *servers = us->servers->elts;

        ngx_http_upstream_available_capacity_server_t *server_conf = ngx_slab_calloc_locked(shpool, sizeof(ngx_http_upstream_available_capacity_server_t));
        server_conf->server   = &servers[i];
        server_conf->capacity = 1;

        ngx_str_t server_address = ngx_string(server_conf->server->name.data);
        ngx_url_t url;
        ngx_memzero(&url, sizeof(ngx_url_t));
        size_t server_address_size = strlen((char *)server_address.data);
        url.url.len  = server_address_size;
        url.url.data = ngx_slab_alloc_locked(shpool, server_address_size);
        url.default_port = 80;
        ngx_cpystrn(url.url.data, server_address.data, server_address_size + 1);

        if (ngx_parse_url_slab(shpool, &url) != NGX_OK) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "cannot parse url : %s", server_address.data);
            return NGX_DECLINED;
        }
        server_conf->addr = *url.addrs;
        server_conf->next = NULL;

        if (prev_server_conf) {
            prev_server_conf->next = server_conf;
        } else {
            conf->server_list = server_conf;
        }
        prev_server_conf = server_conf;
    }

    conf->server_num        = us->servers->nelts;
    conf->search_start_peer = conf->server_list;

    ngx_shmtx_unlock(&shpool->mutex);
    return NGX_OK;
}
ngx_int_t
ngx_http_tfs_parse_tair_server_addr_info(ngx_http_tfs_tair_server_addr_info_t *info,
    u_char *addr, uint32_t len, void *pool, uint8_t shared_memory)
{
    u_char           *temp, *p;
    ssize_t           info_size;
    ngx_int_t         i;

    p = addr;

    for (i = 0; i < NGX_HTTP_TFS_TAIR_SERVER_ADDR_PART_COUNT; i++) {
        temp = ngx_strlchr(p, p + len, ';');
        if (temp == NULL) {
            return NGX_ERROR;
        }

        info_size = temp - p;
        if (shared_memory) {
            info->server[i].data = ngx_slab_alloc_locked((ngx_slab_pool_t *)pool, info_size);
        } else {
            info->server[i].data = ngx_pcalloc((ngx_pool_t *)pool, info_size);
        }
        if (info->server[i].data == NULL) {
            return NGX_ERROR;
        }
        info->server[i].len = info_size;
        memcpy(info->server[i].data, p, info_size);

        p += info_size + 1;
        len -= (info_size + 1);
        if (len <= 0) {
            return NGX_ERROR;
        }
    }

    info->area = ngx_atoi(p, len);
    if (info->area == NGX_ERROR) {
        return NGX_ERROR;
    }

    return NGX_OK;
}
static ngx_btt_peer_info_t *
ngx_btt_get_peer_info(ngx_btt_conf_t *bcf, ngx_btt_ctx_t *ctx)
{
    ngx_queue_t          *q;
    ngx_btt_peer_info_t  *pi;

    if (!ngx_queue_empty(&bcf->btt->free_peer_infos)) {
        q = ngx_queue_head(&bcf->btt->free_peer_infos);
        ngx_queue_remove(q);

        pi = ngx_queue_data(q, ngx_btt_peer_info_t, queue);
        return pi;
    }

    pi = ngx_slab_alloc_locked(bcf->pool, sizeof(ngx_btt_peer_info_t));
    if (pi == NULL) {
        return NULL;
    }

    return pi;
}
static ngx_int_t
ngx_http_push_stream_send_worker_message_locked(ngx_http_push_stream_channel_t *channel, ngx_http_push_stream_queue_elem_t *subscribers_sentinel, ngx_pid_t pid, ngx_int_t worker_slot, ngx_http_push_stream_msg_t *msg, ngx_flag_t *queue_was_empty, ngx_log_t *log)
{
    ngx_slab_pool_t                         *shpool = (ngx_slab_pool_t *) ngx_http_push_stream_shm_zone->shm.addr;
    ngx_http_push_stream_worker_data_t      *workers_data = ((ngx_http_push_stream_shm_data_t *) ngx_http_push_stream_shm_zone->data)->ipc;
    ngx_http_push_stream_worker_data_t      *thisworker_data = workers_data + worker_slot;
    ngx_http_push_stream_worker_msg_t       *newmessage;

    if ((newmessage = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_push_stream_worker_msg_t))) == NULL) {
        ngx_log_error(NGX_LOG_ERR, log, 0, "push stream module: unable to allocate worker message, pid: %P, slot: %d", pid, worker_slot);
        return NGX_ERROR;
    }

    msg->workers_ref_count++;
    newmessage->msg = msg;
    newmessage->pid = pid;
    newmessage->subscribers_sentinel = subscribers_sentinel;
    newmessage->channel = channel;
    *queue_was_empty = ngx_queue_empty(&thisworker_data->messages_queue->queue);
    ngx_queue_insert_tail(&thisworker_data->messages_queue->queue, &newmessage->queue);

    return NGX_OK;
}
static ngx_btt_torrent_t *
ngx_btt_get_torrent(ngx_btt_conf_t *bcf, ngx_btt_ctx_t *ctx, ngx_rbtree_t *tree,
    ngx_queue_t *queue)
{
    ngx_queue_t        *q;
    ngx_btt_torrent_t  *t;

    t = NULL;

    if (!ngx_queue_empty(&bcf->btt->free_torrents)) {
        q = ngx_queue_head(&bcf->btt->free_torrents);
        ngx_queue_remove(q);

        t = ngx_queue_data(q, ngx_btt_torrent_t, queue);
    }

    if (t == NULL) {
        t = ngx_slab_alloc_locked(bcf->pool, sizeof(ngx_btt_torrent_t));
        if (t == NULL) {
            return NULL;
        }
    }

    /* TODO */

    ngx_rbtree_init(&t->peers_rbtree, &t->peers_sentinel, ngx_btt_insert_peer);
    ngx_queue_init(&t->peers_queue);

    ngx_memcpy(t->info_hash, ctx->info_hash, sizeof(ctx->info_hash));

    t->node.key = ngx_crc32_short(ctx->info_hash, sizeof(ctx->info_hash));
    ngx_rbtree_insert(tree, &t->node);
    ngx_queue_insert_head(queue, &t->queue);

    return t;
}
static ngx_int_t
ngx_http_req_status_handler(ngx_http_request_t *r)
{
    size_t                              len;
    uint32_t                            hash;
    ngx_str_t                           key;
    ngx_uint_t                          i;
    ngx_shm_zone_t                    **pzone;
    ngx_pool_cleanup_t                 *cln;
    ngx_http_req_status_ctx_t          *r_ctx;
    ngx_http_req_status_zone_t         *ctx;
    ngx_http_req_status_node_t         *ssn;
    ngx_http_req_status_loc_conf_t     *rlcf;
    ngx_http_req_status_zone_node_t    *pzn;

    r_ctx = ngx_http_get_module_ctx(r, ngx_http_req_status_module);

    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_req_status_module);

    do {
        pzone = rlcf->req_zones.elts;

        for (i = 0; i < rlcf->req_zones.nelts; i++) {
            ctx = pzone[i]->data;

            if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
                continue;
            }

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

            if (key.len > 65535) {
                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                        "req-status, the value of the \"%V\" variable "
                        "is more than 65535 bytes: \"%v\"",
                        &ctx->key.value, &key);
                continue;
            }

            if (r_ctx == NULL) {

                r_ctx = ngx_palloc(r->pool, sizeof(ngx_http_req_status_ctx_t));
                if (r_ctx == NULL) {
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }

                if (ngx_array_init(&r_ctx->req_zones, r->pool, 2,
                            sizeof(ngx_http_req_status_zone_node_t))
                        != NGX_OK)
                {
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }

                cln = ngx_pool_cleanup_add(r->pool, 0);
                if (cln == NULL) {
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }

                cln->handler = ngx_http_req_status_cleanup;
                cln->data = r_ctx;

                ngx_http_set_ctx(r, r_ctx, ngx_http_req_status_module);
            }

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

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

            ssn = ngx_http_req_status_lookup(ctx, hash, &key);

            if (ssn == NULL) {
                len  = sizeof(ngx_http_req_status_node_t) + key.len + 1;

                ssn = ngx_slab_alloc_locked(ctx->shpool, len);
                if (ssn == NULL) {
                    ngx_http_req_status_expire(ctx);

                    ssn = ngx_slab_alloc_locked(ctx->shpool, len);
                    if (ssn == NULL) {
                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                                "req-status, slab alloc fail, zone = \"%V\", "
                                "key = \"%V\", size = %uz",
                                &ctx->shm_zone->shm.name, &key, len);

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

                        continue;
                    }
                }

                ssn->node.key = hash;
                ssn->len = key.len;
                ssn->count = 1;

                ngx_memzero(&ssn->data, sizeof(ssn->data));
                ngx_memcpy(ssn->key, key.data, key.len);
                ssn->key[key.len] = '\0';
                ssn->last_traffic_update = 0;

                ngx_rbtree_insert(&ctx->sh->rbtree, &ssn->node);
            }

            ssn->data.requests ++;
            ssn->active ++;
            if (ssn->active > ssn->data.max_active) {
                ssn->data.max_active = ssn->active;
            }

            ngx_queue_insert_head(&ctx->sh->queue, &ssn->queue);

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

            pzn = ngx_array_push(&r_ctx->req_zones);
            if (pzn == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            pzn->node = ssn;
            pzn->zone = ctx;
        }

        rlcf = rlcf->parent;
    } while (rlcf);

    return NGX_DECLINED;
}
static ngx_int_t
ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash,
    ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account)
{
    size_t                      size;
    ngx_int_t                   rc, excess;
    ngx_msec_t                  now;
    ngx_msec_int_t              ms;
    ngx_rbtree_node_t          *node, *sentinel;
    ngx_http_limit_req_ctx_t   *ctx;
    ngx_http_limit_req_node_t  *lr;

    now = ngx_current_msec;

    ctx = limit->shm_zone->data;

    node = ctx->sh->rbtree.root;
    sentinel = ctx->sh->rbtree.sentinel;

    while (node != sentinel) {

        if (hash < node->key) {
            node = node->left;
            continue;
        }

        if (hash > node->key) {
            node = node->right;
            continue;
        }

        /* hash == node->key */

        lr = (ngx_http_limit_req_node_t *) &node->color;

        rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len);

        if (rc == 0) {
            ngx_queue_remove(&lr->queue);
            ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);

            ms = (ngx_msec_int_t) (now - lr->last);

            excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;

            if (excess < 0) {
                excess = 0;
            }

            *ep = excess;

            if ((ngx_uint_t) excess > limit->burst) {
                return NGX_BUSY;
            }

            if (account) {
                lr->excess = excess;
                lr->last = now;
                return NGX_OK;
            }

            lr->count++;

            ctx->node = lr;

            return NGX_AGAIN;
        }

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

    *ep = 0;

    size = offsetof(ngx_rbtree_node_t, color)
           + offsetof(ngx_http_limit_req_node_t, data)
           + key->len;

    ngx_http_limit_req_expire(ctx, 1);

    node = ngx_slab_alloc_locked(ctx->shpool, size);

    if (node == NULL) {
        ngx_http_limit_req_expire(ctx, 0);

        node = ngx_slab_alloc_locked(ctx->shpool, size);
        if (node == NULL) {
            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
                          "could not allocate node%s", ctx->shpool->log_ctx);
            return NGX_ERROR;
        }
    }

    node->key = hash;

    lr = (ngx_http_limit_req_node_t *) &node->color;

    lr->len = (u_short) key->len;
    lr->excess = 0;

    ngx_memcpy(lr->data, key->data, key->len);

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

    ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);

    if (account) {
        lr->last = now;
        lr->count = 0;
        return NGX_OK;
    }

    lr->last = 0;
    lr->count = 1;

    ctx->node = lr;

    return NGX_AGAIN;
}
ngx_http_reqstat_rbnode_t *
ngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val)
{
    size_t                        size, len;
    uint32_t                      hash;
    ngx_int_t                     rc, excess;
    ngx_time_t                   *tp;
    ngx_msec_t                    now;
    ngx_queue_t                  *q;
    ngx_msec_int_t                ms;
    ngx_rbtree_node_t            *node, *sentinel;
    ngx_http_reqstat_ctx_t       *ctx;
    ngx_http_reqstat_rbnode_t    *rs;

    ctx = shm_zone->data;

    hash = ngx_murmur_hash2(val->data, val->len);

    node = ctx->sh->rbtree.root;
    sentinel = ctx->sh->rbtree.sentinel;

    tp = ngx_timeofday();
    now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);

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

    while (node != sentinel) {

        if (hash < node->key) {
            node = node->left;
            continue;
        }

        if (hash > node->key) {
            node = node->right;
            continue;
        }

        /* hash == node->key */

        rs = (ngx_http_reqstat_rbnode_t *) &node->color;

        /* len < node->len */

        if (val->len < (size_t) rs->len) {
            node = node->left;
            continue;
        }

        rc = ngx_strncmp(val->data, rs->data, (size_t) rs->len);

        if (rc == 0) {

            ms = (ngx_msec_int_t) (now - rs->last_visit);

            rs->excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000
                       + 1000;
            rs->last_visit = now;

            if (rs->excess > 0) {
                ngx_queue_remove(&rs->visit);
                ngx_queue_insert_head(&ctx->sh->visit, &rs->visit);
            }

            ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0,
                           "reqstat lookup exist: %*s", rs->len, rs->data);

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

            return rs;
        }

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

    rc = 0;
    node = NULL;

    size = offsetof(ngx_rbtree_node_t, color)
         + offsetof(ngx_http_reqstat_rbnode_t, data)
         + ctx->key_len;

    if (ctx->alloc_already_fail == 0) {
        node = ngx_slab_alloc_locked(ctx->shpool, size);
        if (node == NULL) {
            ctx->alloc_already_fail = 1;
        }
    }

    if (node == NULL) {

        /* try to free a vacant node */
        q = ngx_queue_last(&ctx->sh->visit);
        rs = ngx_queue_data(q, ngx_http_reqstat_rbnode_t, visit);

        ms = (ngx_msec_int_t) (now - rs->last_visit);

        excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000;

        ngx_log_debug3(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0,
                       "reqstat lookup try recycle: %*s, %d", rs->len, rs->data, excess);

        if (excess < 0) {

            rc = 1;

            node = (ngx_rbtree_node_t *)
                            ((char *) rs - offsetof(ngx_rbtree_node_t, color));
            ngx_rbtree_delete(&ctx->sh->rbtree, node);
            ngx_queue_remove(&rs->visit);

            ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0,
                           "reqstat lookup recycle: %*s", rs->len, rs->data);

            rs->conn_total = 0;

            ngx_memzero((void *) &rs->bytes_in, size - offsetof(ngx_rbtree_node_t, color)
                                            - offsetof(ngx_http_reqstat_rbnode_t, bytes_in));

        } else {
            ngx_shmtx_unlock(&ctx->shpool->mutex);
            return NULL;
        }
    }

    node->key = hash;

    rs = (ngx_http_reqstat_rbnode_t *) &node->color;

    len = ngx_min(ctx->key_len, (ssize_t) val->len);
    ngx_memcpy(rs->data, val->data, len);
    rs->len = len;

    ngx_rbtree_insert(&ctx->sh->rbtree, node);
    ngx_queue_insert_head(&ctx->sh->visit, &rs->visit);
    if (!rc) {
        ngx_queue_insert_head(&ctx->sh->queue, &rs->queue);
    }

    rs->last_visit = now;
    rs->excess = 1000;

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup build: %*s", rs->len, rs->data);

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

    return rs;
}
static ngx_int_t
ngx_http_limit_speed_handler(ngx_http_request_t *r)
{
    size_t                           len, n;
    uint32_t                         hash;
    ngx_int_t                        rc;
    ngx_slab_pool_t                 *shpool;
    ngx_rbtree_node_t               *node, *sentinel;
    ngx_pool_cleanup_t              *cln;
    ngx_http_variable_value_t       *vv;
    ngx_http_limit_speed_ctx_t      *ctx;
    ngx_http_limit_speed_node_t     *ls;
    ngx_http_limit_speed_conf_t     *lscf;
    ngx_http_limit_speed_cleanup_t  *lscln;
    ngx_http_limit_speed_req_ctx_t  *rctx;

    if (r->main->limit_rate) {
        return NGX_DECLINED;
    }

    lscf = ngx_http_get_module_loc_conf(r, ngx_http_limit_speed_module);

    if (lscf->shm_zone == NULL) {
        return NGX_DECLINED;
    }

    ctx = lscf->shm_zone->data;

    vv = ngx_http_get_indexed_variable(r, ctx->index);

    if (vv == NULL || vv->not_found) {
        return NGX_DECLINED;
    }

    len = vv->len;

    if (len == 0) {
        return NGX_DECLINED;
    }

    if (lscf->var_max_len) {
        len = len > lscf->var_max_len ? lscf->var_max_len : len;
    }

    hash = ngx_crc32_short(vv->data, len);

    cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_speed_cleanup_t));
    if (cln == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

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

    ngx_shmtx_lock(&shpool->mutex);

    node = ctx->rbtree->root;
    sentinel = ctx->rbtree->sentinel;

    while (node != sentinel) {

        if (hash < node->key) {
            node = node->left;
            continue;
        }

        if (hash > node->key) {
            node = node->right;
            continue;
        }

        /* hash == node->key */

        do {
            ls = (ngx_http_limit_speed_node_t *) &node->color;

            rc = ngx_memn2cmp(vv->data, ls->data, len, (size_t) ls->len);

            if (rc == 0) {
                ls->conn++;
                goto done;
            }

            node = (rc < 0) ? node->left : node->right;

        } while (node != sentinel && hash == node->key);

        break;
    }

    n = offsetof(ngx_rbtree_node_t, color)
        + offsetof(ngx_http_limit_speed_node_t, data)
        + len;

    node = ngx_slab_alloc_locked(shpool, n);
    if (node == NULL) {
        ngx_shmtx_unlock(&shpool->mutex);
        return NGX_DECLINED;
    }

    ls = (ngx_http_limit_speed_node_t *) &node->color;

    node->key = hash;
    ls->len = (u_char) len;
    ls->conn = 1;
    ngx_memcpy(ls->data, vv->data, len);

    ngx_rbtree_insert(ctx->rbtree, node);

done:

    r->main->limit_rate = lscf->speed / ls->conn;

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "limit speed zone: %08XD conn=%d, speed=%d",
                   node->key, ls->conn, r->main->limit_rate);

    ngx_shmtx_unlock(&shpool->mutex);

    cln->handler = ngx_http_limit_speed_cleanup;
    lscln = cln->data;

    lscln->shm_zone = lscf->shm_zone;
    lscln->node = node;

    if (ngx_http_limit_speed_get_ctx(r) != NGX_OK) {
        return NGX_DECLINED;
    }

    rctx = ngx_http_get_module_ctx(r, ngx_http_limit_speed_module);
    rctx->speed = lscf->speed;
    rctx->ls = ls;

    return NGX_DECLINED;
}
static ngx_int_t 
ngx_http_session_store_by_key(ngx_http_request_t *r)
{
    u_char                             *last;
    size_t                              n, len;
    uint32_t                            hash;
    ngx_int_t                           rc;
    ngx_uint_t                          i, index;
    ngx_rbtree_node_t                  *node;
    ngx_http_variable_value_t          *v, *vv;
    ngx_http_session_store_ctx_t       *ctx;
    ngx_http_session_store_node_t      *ssn;
    ngx_http_session_store_loc_conf_t  *sslcf;

    sslcf = ngx_http_get_module_loc_conf(r, ngx_http_session_store_module);

    if (sslcf->store_shm_zone == NULL) {
        return NGX_DECLINED;
    }

    ctx = sslcf->store_shm_zone->data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
            "session ctx: %p", ctx);

    v = ngx_http_get_indexed_variable(r, ctx->index);

    if (v == NULL || v->not_found) {
        return NGX_DECLINED;
    }

    len = v->len;

    if (len == 0) {
        return NGX_DECLINED;
    }

    if (len > 65535) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "the value of the \"%V\" variable "
                      "is more than 65535 bytes: \"%v\"",
                      &ctx->var, v);
        return NGX_ERROR;
    }

    hash = ngx_crc32_short(v->data, len);

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

    ngx_http_session_store_expire(ctx, 1);

    rc = ngx_http_session_store_lookup(ctx, hash, v->data, len, &ssn);

    /* Find, delete the old one */

    if (ssn) {
        ngx_http_session_store_delete(ctx, ssn);
    };

    /* Not find */

    n = offsetof(ngx_rbtree_node_t, color)
        + offsetof(ngx_http_session_store_node_t, data)
        + len;

    for (i = 0; i < 8; i++) {
        index = sslcf->store_index[i];

        if (index == NGX_CONF_UNSET_UINT) {
            break;
        }

        vv = ngx_http_get_indexed_variable(r, index);

        if (vv == NULL || vv->not_found) {
            break;
        }

        n += vv->len;
    }

    node = ngx_slab_alloc_locked(ctx->shpool, n);
    if (node == NULL) {
        ngx_http_session_store_expire(ctx, 0);

        node = ngx_slab_alloc_locked(ctx->shpool, n);
        if (node == NULL) {
            ngx_shmtx_unlock(&ctx->shpool->mutex);

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 
                    "not enough share memory for zone \"%V\"", 
                    &sslcf->store_shm_zone->shm.name);
            return NGX_ERROR;
        }
    }

    ssn = (ngx_http_session_store_node_t *) &node->color;

    node->key = hash;
    ssn->key_len = (u_char) len;
    ssn->expire = ngx_time() + sslcf->expire;

    last = ssn->data;

    ngx_memcpy(last, v->data, len);
    last += len;

    for (i = 0; i < 8; i++) {
        index = sslcf->store_index[i];

        if (index == NGX_CONF_UNSET_UINT) {
            break;
        }

        vv = ngx_http_get_indexed_variable(r, index);

        if (vv == NULL || vv->not_found) {
            break;
        }

        ssn->record[i].start = last - ssn->data;
        ssn->record[i].length = vv->len;

        ngx_memcpy(last, vv->data, vv->len);
        last += vv->len;
    }

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

    ngx_queue_insert_head(&ctx->sh->queue, &ssn->queue);

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

    return NGX_OK;
}
static void
ngx_stream_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
    void *data)
{
    ngx_stream_upstream_rr_peer_data_t  *rrp = data;

    ngx_ssl_session_t               *old_ssl_session, *ssl_session;
    ngx_stream_upstream_rr_peer_t   *peer;
#if (NGX_STREAM_UPSTREAM_ZONE)
    int                              len;
    u_char                          *p;
    ngx_stream_upstream_rr_peers_t  *peers;
    u_char                           buf[NGX_SSL_MAX_SESSION_SIZE];
#endif

#if (NGX_STREAM_UPSTREAM_ZONE)
    peers = rrp->peers;

    if (peers->shpool) {

        ssl_session = SSL_get0_session(pc->connection->ssl->connection);

        if (ssl_session == NULL) {
            return;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
                       "save session: %p", ssl_session);

        len = i2d_SSL_SESSION(ssl_session, NULL);

        /* do not cache too big session */

        if (len > NGX_SSL_MAX_SESSION_SIZE) {
            return;
        }

        p = buf;
        (void) i2d_SSL_SESSION(ssl_session, &p);

        peer = rrp->current;

        ngx_stream_upstream_rr_peers_rlock(peers);
        ngx_stream_upstream_rr_peer_lock(peers, peer);

        if (len > peer->ssl_session_len) {
            ngx_shmtx_lock(&peers->shpool->mutex);

            if (peer->ssl_session) {
                ngx_slab_free_locked(peers->shpool, peer->ssl_session);
            }

            peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len);

            ngx_shmtx_unlock(&peers->shpool->mutex);

            if (peer->ssl_session == NULL) {
                peer->ssl_session_len = 0;

                ngx_stream_upstream_rr_peer_unlock(peers, peer);
                ngx_stream_upstream_rr_peers_unlock(peers);
                return;
            }

            peer->ssl_session_len = len;
        }

        ngx_memcpy(peer->ssl_session, buf, len);

        ngx_stream_upstream_rr_peer_unlock(peers, peer);
        ngx_stream_upstream_rr_peers_unlock(peers);

        return;
    }
#endif

    ssl_session = ngx_ssl_get_session(pc->connection);

    if (ssl_session == NULL) {
        return;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
                   "save session: %p", ssl_session);

    peer = rrp->current;

    old_ssl_session = peer->ssl_session;
    peer->ssl_session = ssl_session;

    if (old_ssl_session) {

        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
                       "old session: %p", old_ssl_session);

        /* TODO: may block */

        ngx_ssl_free_session(old_ssl_session);
    }
}
int
ngx_tcp_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key,
    size_t key_len, int value_type, u_char *str_value_buf,
    size_t str_value_len, double num_value, int exptime, int user_flags,
    char **errmsg, int *forcible)
{
    int                          i, n;
    u_char                       c, *p;
    uint32_t                     hash;
    ngx_int_t                    rc;
    ngx_time_t                  *tp;
    ngx_rbtree_node_t           *node;
    ngx_tcp_lua_shdict_ctx_t   *ctx;
    ngx_tcp_lua_shdict_node_t  *sd;

    if (zone == NULL) {
        return NGX_ERROR;
    }

    dd("exptime: %d", exptime);

    ctx = zone->data;

    *forcible = 0;

    hash = ngx_crc32_short(key, key_len);

    switch (value_type) {
    case LUA_TSTRING:
        /* do nothing */
        break;

    case LUA_TNUMBER:
        dd("num value: %lf", num_value);
        str_value_buf = (u_char *) &num_value;
        str_value_len = sizeof(double);
        break;

    case LUA_TBOOLEAN:
        c = num_value ? 1 : 0;
        str_value_buf = &c;
        str_value_len = sizeof(u_char);
        break;

    case LUA_TNIL:
        if (op & (NGX_TCP_LUA_SHDICT_ADD|NGX_TCP_LUA_SHDICT_REPLACE)) {
            *errmsg = "attempt to add or replace nil values";
            return NGX_ERROR;
        }

        str_value_buf = NULL;
        str_value_len = 0;
        break;

    default:
        *errmsg = "unsupported value type";
        return NGX_ERROR;
    }

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

#if 1
    ngx_tcp_lua_shdict_expire(ctx, 1);
#endif

    rc = ngx_tcp_lua_shdict_lookup(zone, hash, key, key_len, &sd);

    dd("lookup returns %d", (int) rc);

    if (op & NGX_TCP_LUA_SHDICT_REPLACE) {

        if (rc == NGX_DECLINED || rc == NGX_DONE) {
            ngx_shmtx_unlock(&ctx->shpool->mutex);
            *errmsg = "not found";
            return NGX_DECLINED;
        }
        /* rc == NGX_OK */

        goto replace;
    }

    if (op & NGX_TCP_LUA_SHDICT_ADD) {

        if (rc == NGX_OK) {
            ngx_shmtx_unlock(&ctx->shpool->mutex);
            *errmsg = "exists";
            return NGX_DECLINED;
        }

        if (rc == NGX_DONE) {
            /* exists but expired */

            dd("go to replace");
            goto replace;
        }

        /* rc == NGX_DECLINED */

        dd("go to insert");
        goto insert;
    }

    if (rc == NGX_OK || rc == NGX_DONE) {

        if (value_type == LUA_TNIL) {
            goto remove;
        }

replace:

        if (str_value_buf && str_value_len == (size_t) sd->value_len) {

            ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0,
                           "lua shared dict set: found old entry and value "
                           "size matched, reusing it");

            ngx_queue_remove(&sd->queue);
            ngx_queue_insert_head(&ctx->sh->queue, &sd->queue);

            sd->key_len = (u_short) key_len;

            if (exptime > 0) {
                tp = ngx_timeofday();
                sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
                              + (uint64_t) exptime;

            } else {
                sd->expires = 0;
            }

            sd->user_flags = user_flags;

            sd->value_len = (uint32_t) str_value_len;

            dd("setting value type to %d", value_type);

            sd->value_type = (uint8_t) value_type;

            p = ngx_copy(sd->data, key, key_len);
            ngx_memcpy(p, str_value_buf, str_value_len);

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

            return NGX_OK;
        }

        ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0,
                       "lua shared dict set: found old entry bug value size "
                       "NOT matched, removing it first");

remove:

        ngx_queue_remove(&sd->queue);

        node = (ngx_rbtree_node_t *)
                   ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));

        ngx_rbtree_delete(&ctx->sh->rbtree, node);

        ngx_slab_free_locked(ctx->shpool, node);

    }

insert:

    /* rc == NGX_DECLINED or value size unmatch */

    if (str_value_buf == NULL) {
        ngx_shmtx_unlock(&ctx->shpool->mutex);
        return NGX_OK;
    }

    ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0,
                   "lua shared dict set: creating a new entry");

    n = offsetof(ngx_rbtree_node_t, color)
        + offsetof(ngx_tcp_lua_shdict_node_t, data)
        + key_len
        + str_value_len;

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

    if (node == NULL) {

        if (op & NGX_TCP_LUA_SHDICT_SAFE_STORE) {
            ngx_shmtx_unlock(&ctx->shpool->mutex);

            *errmsg = "no memory";
            return NGX_ERROR;
        }

        ngx_log_debug2(NGX_LOG_DEBUG_TCP, ctx->log, 0,
                       "lua shared dict set: overriding non-expired items "
                       "due to memory shortage for entry \"%*s\"", key_len,
                       key);

        for (i = 0; i < 30; i++) {
            if (ngx_tcp_lua_shdict_expire(ctx, 0) == 0) {
                break;
            }

            *forcible = 1;

            node = ngx_slab_alloc_locked(ctx->shpool, n);
            if (node != NULL) {
                goto allocated;
            }
        }

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

        *errmsg = "no memory";
        return NGX_ERROR;
    }

allocated:

    sd = (ngx_tcp_lua_shdict_node_t *) &node->color;

    node->key = hash;
    sd->key_len = (u_short) key_len;

    if (exptime > 0) {
        tp = ngx_timeofday();
        sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
                      + (uint64_t) exptime;

    } else {
        sd->expires = 0;
    }

    sd->user_flags = user_flags;
    sd->value_len = (uint32_t) str_value_len;
    dd("setting value type to %d", value_type);
    sd->value_type = (uint8_t) value_type;

    p = ngx_copy(sd->data, key, key_len);
    ngx_memcpy(p, str_value_buf, str_value_len);

    ngx_rbtree_insert(&ctx->sh->rbtree, node);
    ngx_queue_insert_head(&ctx->sh->queue, &sd->queue);
    ngx_shmtx_unlock(&ctx->shpool->mutex);

    return NGX_OK;
}
示例#25
0
/*
 * ngx_http_ip_blacklist_update
 *
 * This function updates the blacklisting count according to @addr.
 * If the count reaches max threshold, which is indicated by @max, the source
 * IP address will be added to the real blacklist.
 *
 * @addr: IP address of the request
 * @max: threshold of failure number
 * @module: ngx_module_t of the calling module
 *
 * Return values:
 *     0: not blacklisted
 *     1: blacklisted
 *    -1: error occured
 */
ngx_int_t
ngx_http_ip_blacklist_update(ngx_http_request_t *r,
                             ngx_str_t *addr,
                             ngx_int_t max,
                             ngx_module_t *module)
{
    ngx_http_ip_blacklist_main_conf_t         *imcf;
    ngx_http_ip_blacklist_loc_conf_t          *ilcf;
    ngx_http_ip_blacklist_t                   *node;
    ngx_http_ip_blacklist_tree_t              *blacklist;
    uint32_t                                   hash;
    ngx_int_t                                  i, sys = 0, ret = 0;

    imcf = ngx_http_get_module_main_conf(r, ngx_http_ip_blacklist_module);
    ilcf = ngx_http_get_module_loc_conf(r, ngx_http_ip_blacklist_module);

    if (!imcf->enabled) {
        return 0;
    }

    if (addr->len > NGX_HTTP_IP_BLACKLIST_ADDR_LEN) {
        return -1;
    }

    blacklist = ngx_http_ip_blacklist_shm_zone->data;
    ngx_shmtx_lock(&blacklist->shpool->mutex);

    if (r->ip_blacklist_node) {
        node = r->ip_blacklist_node;
    } else {
        /* maybe other requests set the node, so let's do a lookup */
        hash = ngx_crc32_short(addr->data, addr->len);

        node = ngx_http_ip_blacklist_lookup(&blacklist->blacklist,
                                            addr,
                                            hash);

        if (node == NULL) {
            /* add new rbtree item to record this addr */
            node = ngx_slab_alloc_locked(blacklist->shpool,
                                         sizeof(ngx_http_ip_blacklist_t));
            if (node == NULL) {
                ngx_shmtx_unlock(&blacklist->shpool->mutex);
                return -1;
            }

            memset(node, 0, sizeof(ngx_http_ip_blacklist_t));

            memcpy(node->addr, addr->data, addr->len);
            node->len = (u_short)addr->len;

            node->timeout = ngx_time() + imcf->timeout;

            for (i = 0; i < NGX_HTTP_IP_BLACKLIST_MOD_NUM; i++) {
                if (ngx_http_ip_blacklist_modules[i] != NULL) {
                    node->counts[i].module = ngx_http_ip_blacklist_modules[i];

                    if (ngx_http_ip_blacklist_modules[i] == module) {
                        node->counts[i].count++;
                        break;
                    }
                }
            }

            node->node.key = hash;

            ngx_rbtree_insert(&blacklist->blacklist, &node->node);
            ngx_queue_insert_head(&blacklist->garbage, &node->queue);

            r->ip_blacklist_node = node;
            node->ref++;

            ngx_http_ip_blacklist_request_cleanup_init(r);

            ngx_shmtx_unlock(&blacklist->shpool->mutex);
            return 0;
        }
    }

    if (node->blacklist) {
        /* Perhaps other workers set this IP addr to match with max count */
        ngx_shmtx_unlock(&blacklist->shpool->mutex);
        return 1;
    }

    if (node->timed) {
        /* node is timed out, reuse this node, reset the count */
        node->timed = 0;

        for (i = 0; i < NGX_HTTP_IP_BLACKLIST_MOD_NUM; i++) {
            node->counts[i].count = 0;
        }
    }

    /* otherwise, increase the count and check if it matches with max count */
    for (i = 0; i < NGX_HTTP_IP_BLACKLIST_MOD_NUM; i++) {
        if (node->counts[i].module == module) {
            if (++(node->counts[i].count) == max) {
                if (imcf->mode == NGX_HTTP_BLACKLIST_MODE_LOCAL) {
                    node->blacklist = 1;
                } else {
                    sys = 1;
                }

                ngx_shmtx_unlock(&blacklist->shpool->mutex);

                if (ilcf->log_enabled) {
                    ngx_http_ip_blacklist_write_attack_log(r, addr, sys);
                }

                if (sys == 0) {
                    /* in local mode, just return */
                    return 1;
                }

                /* in sys mode */
                if (imcf->syscmd.len != 0) {
                    /* build up a command and call system */
                    memset(imcf->buf, 0, imcf->buf_len);
                    ngx_snprintf(imcf->buf,
                                 imcf->buf_len, (char *)imcf->syscmd.data, addr);

                    /* TODO: fix this */
                    ret = system((char *)imcf->buf);
                    return (ret == 0) ? 1 : -1;
                }

                /* no sys comamnd provided */
                return 0;
            }
            break;
        }
    }

    ngx_shmtx_unlock(&blacklist->shpool->mutex);

    return 0;
}
示例#26
0
static ngx_int_t
ngx_http_ip_behavior_lookup(ngx_http_request_t *r, ngx_uint_t hash,
    ngx_http_ip_behavior_ctx_t *ctx, ngx_http_ip_behavior_conf_t *ilcf)
{
    size_t                           size;
    ngx_int_t                        rc;
    ngx_time_t                      *tp;
    ngx_msec_t                       now;
    ngx_rbtree_node_t               *node, *sentinel;
    ngx_http_ip_behavior_node_t     *ibn;

    tp = ngx_timeofday();
    now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);

    node = ctx->sh->rbtree.root;
    sentinel = ctx->sh->rbtree.sentinel;
    rc = -1;

    while (node != sentinel) {

        if (hash < node->key) {
            node = node->left;
            continue;
        }

        if (hash > node->key) {
            node = node->right;
            continue;
        }

        /* hash == node->key */

        ibn = (ngx_http_ip_behavior_node_t *) &node->color;

        rc = ngx_memn2cmp(r->connection->addr_text.data, ibn->addr,
                r->connection->addr_text.len, (size_t) ibn->len);

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "ip behavior lookup is : %i", rc);

        if (rc == 0) {
            /* found the node */

            /* check expire */
            if (ngx_abs((ngx_msec_int_t)(now - ibn->last))
                    >= ctx->sample_cycle) {
                /* node is expired, clear counters */
                ibn->insensitive = 0;
                ibn->total = 0;
                ibn->bad_response = 0;
            }

            ngx_queue_remove(&ibn->queue);
            ngx_queue_insert_head(&ctx->sh->queue, &ibn->queue);

            ibn->last = now;

            if (ilcf->type & TYPE_SENSITIVE_URL) {
                if (!ilcf->sensitive) {
                    ibn->insensitive++;
                }
            } else if (ilcf->type & TYPE_BAD_RESPONSE) {
                /* TODO: support bad response behavior */
            } else {
                /* should never be here */
            }

            /* total can be zero when it grows to big,
             * so need to reset all the counters */
            if (++ibn->total == 0) {
                ibn->insensitive = 0;
                ibn->bad_response = 0;

                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "ip behavior node reset: %V",
                        &r->connection->addr_text);
            }

            if (ibn->total >= (ngx_uint_t)ctx->sample_base) {
                r->insensitive_percent =
                    (float)ibn->insensitive / (float)ibn->total * 100;
                r->bad_response_percent =
                    (float)ibn->bad_response / (float)ibn->total * 100;
            }

            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                          "ip behavior find node: %V, total: %i, insens: %i,"
                          " percent: %i",
                          &r->connection->addr_text, ibn->total,
                          ibn->insensitive, r->insensitive_percent);

            return NGX_OK;
        }

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

    /* create a new node */
    size = offsetof(ngx_rbtree_node_t, color)
           + offsetof(ngx_http_ip_behavior_node_t, addr)
           + r->connection->addr_text.len;

    node = ngx_slab_alloc_locked(ctx->shpool, size);

    if (node == NULL) {
        ngx_http_ip_behavior_expire(ctx, 1);

        node = ngx_slab_alloc_locked(ctx->shpool, size);
        if (node == NULL) {
            return NGX_ERROR;
        }
    }

    node->key = hash;

    ibn = (ngx_http_ip_behavior_node_t *) &node->color;

    ibn->len = (u_char) r->connection->addr_text.len;

    ngx_memcpy(ibn->addr, r->connection->addr_text.data,
            r->connection->addr_text.len);

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

    ngx_queue_insert_head(&ctx->sh->queue, &ibn->queue);

    ibn->last = now;
    ibn->total = 1;
    ibn->insensitive = !ilcf->sensitive;
    ibn->bad_response = 0;

    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
            "ip behavior new node: %V, total: %i, insens: %i,"
            " percent: %i",
            &r->connection->addr_text, ibn->total,
            ibn->insensitive, r->insensitive_percent);

    return NGX_OK;
}
示例#27
0
static ngx_int_t
ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
{
    ngx_int_t                    rc;
    ngx_http_file_cache_node_t  *fcn;

    ngx_shmtx_lock(&cache->shpool->mutex);

    fcn = ngx_http_file_cache_lookup(cache, c->key);

    if (fcn) {
        ngx_queue_remove(&fcn->queue);

        if (fcn->error) {

            if (fcn->valid_sec < ngx_time()) {
                goto renew;
            }

            rc = NGX_OK;

            goto done;
        }

        fcn->uses++;
        fcn->count++;

        if (fcn->exists) {

            c->exists = fcn->exists;
            c->body_start = fcn->body_start;

            rc = NGX_OK;

            goto done;
        }

        if (fcn->uses >= c->min_uses) {

            c->exists = fcn->exists;
            c->body_start = fcn->body_start;

            rc = NGX_OK;

        } else {
            rc = NGX_AGAIN;
        }

        goto done;
    }

    fcn = ngx_slab_alloc_locked(cache->shpool,
                                sizeof(ngx_http_file_cache_node_t));
    if (fcn == NULL) {
        ngx_shmtx_unlock(&cache->shpool->mutex);

        (void) ngx_http_file_cache_forced_expire(cache);

        ngx_shmtx_lock(&cache->shpool->mutex);

        fcn = ngx_slab_alloc_locked(cache->shpool,
                                    sizeof(ngx_http_file_cache_node_t));
        if (fcn == NULL) {
            rc = NGX_ERROR;
            goto failed;
        }
    }

    ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));

    ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
               NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));

    ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);

renew:

    rc = NGX_DECLINED;

    fcn->uses = 1;
    fcn->count = 1;
    fcn->valid_msec = 0;
    fcn->error = 0;
    fcn->exists = 0;
    fcn->valid_sec = 0;
    fcn->uniq = 0;
    fcn->body_start = 0;
    fcn->length = 0;

done:

    fcn->expire = ngx_time() + cache->inactive;

    ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

    c->uniq = fcn->uniq;
    c->error = fcn->error;
    c->node = fcn;

failed:

    ngx_shmtx_unlock(&cache->shpool->mutex);

    return rc;
}
static ngx_int_t
ngx_http_limit_zone_handler(ngx_http_request_t *r)
{
    size_t                          len, n;
    uint32_t                        hash;
    ngx_int_t                       rc;
    ngx_slab_pool_t                *shpool;
    ngx_rbtree_node_t              *node, *sentinel;
    ngx_pool_cleanup_t             *cln;
    ngx_http_variable_value_t      *vv;
    ngx_http_limit_zone_ctx_t      *ctx;
    ngx_http_limit_zone_node_t     *lz;
    ngx_http_limit_zone_conf_t     *lzcf;
    ngx_http_limit_zone_cleanup_t  *lzcln;

    if (r->main->limit_zone_set) {
        return NGX_DECLINED;
    }

    lzcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_zone_module);

    if (lzcf->shm_zone == NULL) {
        return NGX_DECLINED;
    }

    ctx = lzcf->shm_zone->data;

    vv = ngx_http_get_indexed_variable(r, ctx->index);

    if (vv == NULL || vv->not_found) {
        return NGX_DECLINED;
    }

    len = vv->len;

    if (len == 0) {
        return NGX_DECLINED;
    }

    if (len > 255) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "the value of the \"%V\" variable "
                      "is more than 255 bytes: \"%v\"",
                      &ctx->var, vv);
        return NGX_DECLINED;
    }

    r->main->limit_zone_set = 1;

    hash = ngx_crc32_short(vv->data, len);

    cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_zone_cleanup_t));
    if (cln == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

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

    ngx_shmtx_lock(&shpool->mutex);

    node = ctx->rbtree->root;
    sentinel = ctx->rbtree->sentinel;

    while (node != sentinel) {

        if (hash < node->key) {
            node = node->left;
            continue;
        }

        if (hash > node->key) {
            node = node->right;
            continue;
        }

        /* hash == node->key */

        do {
            lz = (ngx_http_limit_zone_node_t *) &node->color;

            rc = ngx_memn2cmp(vv->data, lz->data, len, (size_t) lz->len);

            if (rc == 0) {
                if ((ngx_uint_t) lz->conn < lzcf->conn) {
                    lz->conn++;
                    goto done;
                }

                ngx_shmtx_unlock(&shpool->mutex);

                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                              "limiting connections by zone \"%V\"",
                              &lzcf->shm_zone->shm.name);

                return NGX_HTTP_SERVICE_UNAVAILABLE;
            }

            node = (rc < 0) ? node->left : node->right;

        } while (node != sentinel && hash == node->key);

        break;
    }

    n = offsetof(ngx_rbtree_node_t, color)
        + offsetof(ngx_http_limit_zone_node_t, data)
        + len;

    node = ngx_slab_alloc_locked(shpool, n);
    if (node == NULL) {
        ngx_shmtx_unlock(&shpool->mutex);
        return NGX_HTTP_SERVICE_UNAVAILABLE;
    }

    lz = (ngx_http_limit_zone_node_t *) &node->color;

    node->key = hash;
    lz->len = (u_char) len;
    lz->conn = 1;
    ngx_memcpy(lz->data, vv->data, len);

    ngx_rbtree_insert(ctx->rbtree, node);

done:

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "limit zone: %08XD %d", node->key, lz->conn);

    ngx_shmtx_unlock(&shpool->mutex);

    cln->handler = ngx_http_limit_zone_cleanup;
    lzcln = cln->data;

    lzcln->shm_zone = lzcf->shm_zone;
    lzcln->node = node;

    return NGX_DECLINED;
}
static int
ngx_tcp_lua_shdict_set_helper(lua_State *L, int flags)
{
    int                          i, n;
    ngx_str_t                    key;
    uint32_t                     hash;
    ngx_int_t                    rc;
    ngx_tcp_lua_shdict_ctx_t   *ctx;
    ngx_tcp_lua_shdict_node_t  *sd;
    ngx_str_t                    value;
    int                          value_type;
    double                       num;
    u_char                       c;
    lua_Number                   exptime = 0;
    u_char                      *p;
    ngx_rbtree_node_t           *node;
    ngx_time_t                  *tp;
    ngx_shm_zone_t              *zone;
    int                          forcible = 0;
                         /* indicates whether to foricibly override other
                          * valid entries */
    int32_t                      user_flags = 0;

    n = lua_gettop(L);

    if (n != 3 && n != 4 && n != 5) {
        return luaL_error(L, "expecting 3, 4 or 5 arguments, "
                          "but only seen %d", n);
    }

    zone = lua_touserdata(L, 1);
    if (zone == NULL) {
        return luaL_error(L, "bad \"zone\" argument");
    }

    ctx = zone->data;

    if (lua_isnil(L, 2)) {
        lua_pushnil(L);
        lua_pushliteral(L, "nil key");
        return 2;
    }

    key.data = (u_char *) luaL_checklstring(L, 2, &key.len);

    if (key.len == 0) {
        lua_pushnil(L);
        lua_pushliteral(L, "empty key");
        return 2;
    }

    if (key.len > 65535) {
        lua_pushnil(L);
        lua_pushliteral(L, "key too long");
        return 2;
    }

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

    value_type = lua_type(L, 3);

    switch (value_type) {
    case LUA_TSTRING:
        value.data = (u_char *) lua_tolstring(L, 3, &value.len);
        break;

    case LUA_TNUMBER:
        value.len = sizeof(double);
        num = lua_tonumber(L, 3);
        value.data = (u_char *) &num;
        break;

    case LUA_TBOOLEAN:
        value.len = sizeof(u_char);
        c = lua_toboolean(L, 3) ? 1 : 0;
        value.data = &c;
        break;

    case LUA_TNIL:
        if (flags & (NGX_TCP_LUA_SHDICT_ADD|NGX_TCP_LUA_SHDICT_REPLACE)) {
            lua_pushnil(L);
            lua_pushliteral(L, "attempt to add or replace nil values");
            return 2;
        }

        ngx_str_null(&value);
        break;

    default:
        lua_pushnil(L);
        lua_pushliteral(L, "bad value type");
        return 2;
    }

    if (n >= 4) {
        exptime = luaL_checknumber(L, 4);
        if (exptime < 0) {
            exptime = 0;
        }
    }

    if (n == 5) {
        user_flags = (uint32_t) luaL_checkinteger(L, 5);
    }

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

#if 1
    ngx_tcp_lua_shdict_expire(ctx, 1);
#endif

    rc = ngx_tcp_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);

    dd("shdict lookup returned %d", (int) rc);

    if (flags & NGX_TCP_LUA_SHDICT_REPLACE) {

        if (rc == NGX_DECLINED || rc == NGX_DONE) {
            ngx_shmtx_unlock(&ctx->shpool->mutex);

            lua_pushboolean(L, 0);
            lua_pushliteral(L, "not found");
            lua_pushboolean(L, forcible);
            return 3;
        }

        /* rc == NGX_OK */

        goto replace;
    }

    if (flags & NGX_TCP_LUA_SHDICT_ADD) {

        if (rc == NGX_OK) {
            ngx_shmtx_unlock(&ctx->shpool->mutex);

            lua_pushboolean(L, 0);
            lua_pushliteral(L, "exists");
            lua_pushboolean(L, forcible);
            return 3;
        }

        if (rc == NGX_DONE) {
            /* exists but expired */

            dd("go to replace");
            goto replace;
        }

        /* rc == NGX_DECLINED */

        dd("go to insert");
        goto insert;
    }

    if (rc == NGX_OK || rc == NGX_DONE) {

        if (value_type == LUA_TNIL) {
            goto remove;
        }

replace:

        if (value.data && value.len == (size_t) sd->value_len) {

            ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0,
                           "lua shared dict set: found old entry and value "
                           "size matched, reusing it");

            ngx_queue_remove(&sd->queue);
            ngx_queue_insert_head(&ctx->sh->queue, &sd->queue);

            sd->key_len = (u_short) key.len;

            if (exptime > 0) {
                tp = ngx_timeofday();
                sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
                              + (uint64_t) (exptime * 1000);

            } else {
                sd->expires = 0;
            }

            sd->user_flags = user_flags;

            sd->value_len = (uint32_t) value.len;

            dd("setting value type to %d", value_type);

            sd->value_type = (uint8_t) value_type;

            p = ngx_copy(sd->data, key.data, key.len);
            ngx_memcpy(p, value.data, value.len);

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

            lua_pushboolean(L, 1);
            lua_pushnil(L);
            lua_pushboolean(L, forcible);
            return 3;
        }

        ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0,
                       "lua shared dict set: found old entry bug value size "
                       "NOT matched, removing it first");

remove:

        ngx_queue_remove(&sd->queue);

        node = (ngx_rbtree_node_t *)
                   ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));

        ngx_rbtree_delete(&ctx->sh->rbtree, node);

        ngx_slab_free_locked(ctx->shpool, node);

    }

insert:

    /* rc == NGX_DECLINED or value size unmatch */

    if (value.data == NULL) {
        ngx_shmtx_unlock(&ctx->shpool->mutex);

        lua_pushboolean(L, 1);
        lua_pushnil(L);
        lua_pushboolean(L, 0);
        return 3;
    }

    ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0,
                   "lua shared dict set: creating a new entry");

    n = offsetof(ngx_rbtree_node_t, color)
        + offsetof(ngx_tcp_lua_shdict_node_t, data)
        + key.len
        + value.len;

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

    if (node == NULL) {

        if (flags & NGX_TCP_LUA_SHDICT_SAFE_STORE) {
            ngx_shmtx_unlock(&ctx->shpool->mutex);

            lua_pushboolean(L, 0);
            lua_pushliteral(L, "no memory");
            return 2;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_TCP, ctx->log, 0,
                       "lua shared dict set: overriding non-expired items "
                       "due to memory shortage for entry \"%V\"", &key);

        for (i = 0; i < 30; i++) {
            if (ngx_tcp_lua_shdict_expire(ctx, 0) == 0) {
                break;
            }

            forcible = 1;

            node = ngx_slab_alloc_locked(ctx->shpool, n);
            if (node != NULL) {
                goto allocated;
            }
        }

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

        lua_pushboolean(L, 0);
        lua_pushliteral(L, "no memory");
        lua_pushboolean(L, forcible);
        return 3;
    }

allocated:

    sd = (ngx_tcp_lua_shdict_node_t *) &node->color;

    node->key = hash;
    sd->key_len = (u_short) key.len;

    if (exptime > 0) {
        tp = ngx_timeofday();
        sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
                      + (uint64_t) (exptime * 1000);

    } else {
        sd->expires = 0;
    }

    sd->user_flags = user_flags;

    sd->value_len = (uint32_t) value.len;

    dd("setting value type to %d", value_type);

    sd->value_type = (uint8_t) value_type;

    p = ngx_copy(sd->data, key.data, key.len);
    ngx_memcpy(p, value.data, value.len);

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

    ngx_queue_insert_head(&ctx->sh->queue, &sd->queue);

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

    lua_pushboolean(L, 1);
    lua_pushnil(L);
    lua_pushboolean(L, forcible);
    return 3;
}
static ngx_int_t
ngx_http_limit_req2_handler(ngx_http_request_t *r)
{
    size_t                         n, total_len;
    uint32_t                       hash;
    ngx_int_t                      rc;
    ngx_msec_t                     delay_time;
    ngx_uint_t                     excess, delay_excess, delay_postion,
                                   nodelay, i;
    ngx_time_t                    *tp;
    ngx_rbtree_node_t             *node;
    ngx_http_limit_req2_t         *limit_req2;
    ngx_http_limit_req2_ctx_t     *ctx;
    ngx_http_limit_req2_node_t    *lr;
    ngx_http_limit_req2_conf_t    *lrcf;

    delay_excess = 0;
    excess = 0;
    delay_postion = 0;
    nodelay = 0;
    ctx = NULL;
    rc = NGX_DECLINED;

    if (r->main->limit_req_set) {
        return NGX_DECLINED;
    }

    lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req2_module);
    if (lrcf->rules == NULL) {
        return NGX_DECLINED;
    }

    if (!lrcf->enable) {
        return NGX_DECLINED;
    }

    /* filter whitelist */
    if (ngx_http_limit_req2_ip_filter(r, lrcf) == NGX_OK) {
        return NGX_DECLINED;
    }

    /* to match limit_req2 rule*/
    limit_req2 = lrcf->rules->elts;
    for (i = 0; i < lrcf->rules->nelts; i++) {
        ctx = limit_req2[i].shm_zone->data;

        ngx_crc32_init(hash);
        total_len = 0;

        total_len = ngx_http_limit_req2_copy_variables(r, &hash, ctx, NULL);
        if (total_len == 0) {
            continue;
        }

        ngx_crc32_final(hash);

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

        ngx_http_limit_req2_expire(r, ctx, 1);

        rc = ngx_http_limit_req2_lookup(r, &limit_req2[i], hash, &excess);

        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "limit_req2 module: %i %ui.%03ui "
                       "hash is %ui total_len is %i",
                       rc, excess / 1000, excess % 1000, hash, total_len);

        /* first limit_req2 */
        if (rc == NGX_DECLINED) {

            n = offsetof(ngx_rbtree_node_t, color)
                + offsetof(ngx_http_limit_req2_node_t, data)
                + total_len;

            node = ngx_slab_alloc_locked(ctx->shpool, n);
            if (node == NULL) {
                ngx_http_limit_req2_expire(r, ctx, 0);
                node = ngx_slab_alloc_locked(ctx->shpool, n);
                if (node == NULL) {
                    ngx_shmtx_unlock(&ctx->shpool->mutex);
                    return NGX_HTTP_SERVICE_UNAVAILABLE;
                }
            }

            lr = (ngx_http_limit_req2_node_t *) &node->color;

            node->key = hash;
            lr->len = (u_char) total_len;

            tp = ngx_timeofday();
            lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);

            lr->excess = 0;
            ngx_http_limit_req2_copy_variables(r, &hash, ctx, lr);

            ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
            ngx_rbtree_insert(&ctx->sh->rbtree, node);

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

            continue;
        }

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

        if (rc == NGX_BUSY || rc == NGX_ERROR) {
            break;
        }

        /* NGX_AGAIN or NGX_OK */

        if (delay_excess < excess) {
            delay_excess = excess;
            nodelay = limit_req2[i].nodelay;
            delay_postion = i;
        }
    }

    r->main->limit_req_set = 1;

    if (rc == NGX_BUSY || rc == NGX_ERROR) {
        if (rc == NGX_BUSY) {
            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, excess: %ui.%03ui by zone \"%V\"",
                          excess / 1000, excess % 1000,
                          &limit_req2[i].shm_zone->shm.name);
        }

        if (rc == NGX_ERROR || limit_req2[i].forbid_action.len == 0) {

            return NGX_HTTP_SERVICE_UNAVAILABLE;
        } else if (limit_req2[i].forbid_action.data[0] == '@') {

            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, forbid_action is %V",
                          &limit_req2[i].forbid_action);
            (void) ngx_http_named_location(r, &limit_req2[i].forbid_action);

        } else {

            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, forbid_action is %V",
                          &limit_req2[i].forbid_action);
            (void) ngx_http_internal_redirect(r,
                                             &limit_req2[i].forbid_action,
                                             &r->args);
        }

        ngx_http_finalize_request(r, NGX_DONE);
        return NGX_DONE;
    }

    /* rc = NGX_AGAIN */
    if (delay_excess != 0) {

        if (nodelay) {
            return NGX_DECLINED;
        }

        delay_time = (ngx_msec_t) delay_excess * 1000 / ctx->rate;
        ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
                      "delaying request,"
                      "excess: %ui.%03ui, by zone \"%V\", delay \"%ui\" s",
                      delay_excess / 1000, delay_excess % 1000,
                      &limit_req2[delay_postion].shm_zone->shm.name, delay_time);

        if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        r->read_event_handler = ngx_http_test_reading;
        r->write_event_handler = ngx_http_limit_req2_delay;
        ngx_add_timer(r->connection->write, delay_time);

        return NGX_AGAIN;
    }

    /* rc == NGX_OK or rc == NGX_DECLINED */

    return NGX_DECLINED;
}