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;
}
static ngx_int_t
ngx_http_vhost_traffic_status_shm_add_upstream(ngx_http_request_t *r,
        ngx_http_vhost_traffic_status_ctx_t *ctx,
        ngx_http_core_srv_conf_t *cscf,
        ngx_http_vhost_traffic_status_loc_conf_t *vtscf)
{
    u_char                                      *p;
    size_t                                      size;
    uint32_t                                    hash;
    ngx_uint_t                                  i;
    ngx_msec_int_t                              ms;
    ngx_str_t                                   key;
    ngx_slab_pool_t                             *shpool;
    ngx_rbtree_node_t                           *node;
    ngx_http_vhost_traffic_status_node_t        *vtsn;
    ngx_http_upstream_srv_conf_t                *uscf;
    ngx_http_upstream_t                         *u;
    ngx_http_upstream_state_t                   *state;

    if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
        return NGX_OK;
    }

    u = r->upstream;

    if (u->resolved == NULL) {
        uscf = u->conf->upstream;
    } else {
        return NGX_ERROR;
    }

    state = r->upstream_states->elts;

    i = 0;
    ms = 0;
    for ( ;; ) {
        if (state[i].status) {
            ms += (ngx_msec_int_t)
                (state[i].response_sec * 1000 + state[i].response_msec);
        }
        if (++i == r->upstream_states->nelts) {
            break;
        }
    }
    ms = ngx_max(ms, 0);

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

    key.len = (uscf->port ? 0 : uscf->host.len) + sizeof("@") - 1 + state[0].peer->len;
    key.data = ngx_pnalloc(r->pool, key.len);
    if (key.data == NULL) {
        return NGX_ERROR;
    }

    p = key.data;
    if (uscf->port) {
        *p++ = '@';
        p = ngx_cpymem(p, state[0].peer->data, state[0].peer->len);
    } else {
        p = ngx_cpymem(p, uscf->host.data, uscf->host.len);
        *p++ = '@';
        p = ngx_cpymem(p, state[0].peer->data, state[0].peer->len);
    }

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

    if (vtscf->vtsn_upstream && vtscf->vtsn_hash == hash) {
        ngx_shmtx_lock(&shpool->mutex);

        ngx_vhost_traffic_status_node_set(r, vtscf->vtsn_upstream);

        vtscf->vtsn_upstream->stat_upstream.rtms = (ngx_msec_t) (vtscf->vtsn_upstream->stat_upstream.rtms + ms) / 2 +
            (vtscf->vtsn_upstream->stat_upstream.rtms + ms) % 2;

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

    ngx_shmtx_lock(&shpool->mutex);

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

    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_shmtx_unlock(&shpool->mutex);
            return NGX_ERROR;
        }

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

        node->key = hash;

        vtsn->len = (u_char) key.len;
        ngx_vhost_traffic_status_node_init(r, vtsn);

        vtsn->stat_upstream.rtms = ms;
        vtsn->stat_upstream.type = uscf->port ?
            NGX_HTTP_VHOST_TRAFFIC_STATUS_UPSTREAM_UA :
            NGX_HTTP_VHOST_TRAFFIC_STATUS_UPSTREAM_UG;

        ngx_memcpy(vtsn->data, key.data, key.len);
        ngx_rbtree_insert(ctx->rbtree, node);
    } else {
        vtsn = (ngx_http_vhost_traffic_status_node_t *) &node->color;

        ngx_vhost_traffic_status_node_set(r, vtsn);

        vtsn->stat_upstream.rtms = (ngx_msec_t) (vtsn->stat_upstream.rtms + ms) / 2 +
            (vtsn->stat_upstream.rtms + ms) % 2;
    }

    vtscf->vtsn_upstream = vtsn;
    vtscf->vtsn_hash = hash;

    ngx_shmtx_unlock(&shpool->mutex);

    return NGX_OK;
}
static u_char *
ngx_http_vhost_traffic_status_display_set_upstream_group(ngx_http_request_t *r,
        ngx_rbtree_t *rbtree, const char *fmt, u_char *buf,
        ngx_http_vhost_traffic_status_loc_conf_t *vtscf)
{
    size_t                                  len;
    u_char                                  *p, *o, *s;
    uint32_t                                hash;
    ngx_uint_t                              i, j;
    ngx_str_t                               key;
    ngx_rbtree_node_t                       *node;
    ngx_http_upstream_server_t              *us;
    ngx_http_upstream_main_conf_t           *umcf;
    ngx_http_upstream_srv_conf_t            *uscf, **uscfp;
    ngx_http_vhost_traffic_status_node_t    *vtsn;

    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
    uscfp = umcf->upstreams.elts;

    len = 0;
    for (i = 0; i < umcf->upstreams.nelts; i++) {
        uscf = uscfp[i];
        len = ngx_max(uscf->host.len, len);
    }

    key.len = len + sizeof("@[ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255]:65535") - 1;
    key.data = ngx_pnalloc(r->pool, key.len);
    if (key.data == NULL) {
        return buf;
    }

    p = key.data;

    for (i = 0; i < umcf->upstreams.nelts; i++) {

        uscf = uscfp[i];

        /* groups */
        if (uscf->servers && !uscf->port) {
            us = uscf->servers->elts;

            o = buf;

            buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_ARRAY_S,
                    &uscf->host);
            s = buf;

            for (j = 0; j < uscf->servers->nelts; j++) {

                p = ngx_cpymem(p, uscf->host.data, uscf->host.len);
                *p++ = '@';
                p = ngx_cpymem(p, us[j].addrs->name.data, us[j].addrs->name.len);
                key.len = uscf->host.len + sizeof("@") - 1 + us[j].addrs->name.len;
                hash = ngx_crc32_short(key.data, key.len);
                node = ngx_http_vhost_traffic_status_node_lookup(rbtree, &key, hash);

                if (node != NULL) {
                    vtsn = (ngx_http_vhost_traffic_status_node_t *) &node->color;
                    buf = ngx_sprintf(buf, fmt,
                    &us[j].addrs->name,
                    vtsn->stat_request_counter, vtsn->stat_in_bytes, vtsn->stat_out_bytes,
                    vtsn->stat_1xx_counter, vtsn->stat_2xx_counter, vtsn->stat_3xx_counter,
                    vtsn->stat_4xx_counter, vtsn->stat_5xx_counter, vtsn->stat_upstream.rtms,
                    us[j].weight, us[j].max_fails, us[j].fail_timeout,
                    ngx_vhost_traffic_status_boolean_to_string(us[j].backup),
                    ngx_vhost_traffic_status_boolean_to_string(us[j].down));
                } else {
                    buf = ngx_sprintf(buf, fmt,
                    &us[j].addrs->name,
                    0, 0, 0,
                    0, 0, 0,
                    0, 0, (ngx_msec_t) 0,
                    us[j].weight, us[j].max_fails, us[j].fail_timeout,
                    ngx_vhost_traffic_status_boolean_to_string(us[j].backup),
                    ngx_vhost_traffic_status_boolean_to_string(us[j].down));
                }

                p = key.data;
            }

            if (s == buf) {
                buf = o;
            } else {
                buf--;
                buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_ARRAY_E);
                buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_NEXT);
            }
        }
    }

    /* alones */
    o = buf;

    ngx_str_set(&key, "::nogroups");

    buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_ARRAY_S, &key);

    s = buf;

    buf = ngx_http_vhost_traffic_status_display_set_upstream_alone(r,
        rbtree->root, rbtree->sentinel, fmt, buf, vtscf);

    if (s == buf) {
        buf = o;
    } else {
        buf--;
        buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_ARRAY_E);
        buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_NEXT);
    }

    return buf;
}
static ngx_int_t
ngx_http_vhost_traffic_status_shm_add_server(ngx_http_request_t *r,
        ngx_http_vhost_traffic_status_ctx_t *ctx,
        ngx_http_core_srv_conf_t *cscf,
        ngx_http_vhost_traffic_status_loc_conf_t *vtscf)
{
    size_t                                      size;
    uint32_t                                    hash;
    ngx_str_t                                   key;
    ngx_slab_pool_t                             *shpool;
    ngx_rbtree_node_t                           *node;
    ngx_http_vhost_traffic_status_node_t        *vtsn;

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

    if (vtscf->vtsn_server) {
        ngx_shmtx_lock(&shpool->mutex);

        ngx_vhost_traffic_status_node_set(r, vtscf->vtsn_server);

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

    key = cscf->server_name;

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

    ngx_shmtx_lock(&shpool->mutex);

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

    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_shmtx_unlock(&shpool->mutex);
            return NGX_ERROR;
        }

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

        node->key = hash;

        vtsn->len = (u_char) key.len;
        ngx_vhost_traffic_status_node_init(r, vtsn);
        ngx_memcpy(vtsn->data, key.data, key.len);
        ngx_rbtree_insert(ctx->rbtree, node);
    } else {
        vtsn = (ngx_http_vhost_traffic_status_node_t *) &node->color;
        ngx_vhost_traffic_status_node_set(r, vtsn);
    }

    vtscf->vtsn_server = vtsn;

    ngx_shmtx_unlock(&shpool->mutex);

    return NGX_OK;
}