ngx_flag_t ngx_buffer_cache_store_gather( ngx_buffer_cache_t* cache, u_char* key, ngx_str_t* buffers, size_t buffer_count) { ngx_buffer_cache_entry_t* entry; ngx_buffer_cache_sh_t *sh = cache->sh; ngx_str_t* cur_buffer; ngx_str_t* last_buffer; size_t buffer_size; uint32_t hash; uint32_t evictions; u_char* target_buffer; hash = ngx_crc32_short(key, BUFFER_CACHE_KEY_SIZE); ngx_shmtx_lock(&cache->shpool->mutex); if (sh->reset) { // a previous store operation was killed in progress, need to reset the cache // since the data structures may be corrupt. we can only reset the cache after // the access time expires since other processes may still be reading from / // writing to the cache if (ngx_time() < sh->access_time + CACHE_LOCK_EXPIRATION) { ngx_shmtx_unlock(&cache->shpool->mutex); return 0; } // reset the cache, leave the reset flag enabled ngx_buffer_cache_reset(sh); // update stats sh->stats.reset++; } else { // remove expired entries if (cache->expiration) { for (evictions = MAX_EVICTIONS_PER_STORE; evictions > 0; evictions--) { if (!ngx_buffer_cache_free_oldest_entry(sh, cache->expiration)) { break; } } } // make sure the entry does not already exist entry = ngx_buffer_cache_rbtree_lookup(&sh->rbtree, key, hash); if (entry != NULL) { sh->stats.store_exists++; ngx_shmtx_unlock(&cache->shpool->mutex); return 0; } // enable the reset flag before we start making any changes sh->reset = 1; } // allocate a new entry entry = ngx_buffer_cache_get_free_entry(sh); if (entry == NULL) { goto error; } // calculate the buffer size last_buffer = buffers + buffer_count; buffer_size = 0; for (cur_buffer = buffers; cur_buffer < last_buffer; cur_buffer++) { buffer_size += cur_buffer->len; } // allocate a buffer to hold the data target_buffer = ngx_buffer_cache_get_free_buffer(sh, buffer_size); if (target_buffer == NULL) { goto error; } // initialize the entry entry->state = CES_ALLOCATED; entry->node.key = hash; memcpy(entry->key, key, BUFFER_CACHE_KEY_SIZE); entry->start_offset = target_buffer; entry->buffer_size = buffer_size; // update the write position sh->buffers_write = target_buffer; // move from free_queue to used_queue ngx_queue_remove(&entry->queue_node); ngx_queue_insert_tail(&sh->used_queue, &entry->queue_node); // insert to rbtree ngx_rbtree_insert(&sh->rbtree, &entry->node); // update stats sh->stats.store_ok++; sh->stats.store_bytes += buffer_size; // Note: the memcpy is performed after releasing the lock to avoid holding the lock for a long time // setting the access time of the entry and cache prevents it from being freed sh->access_time = entry->access_time = ngx_time(); entry->write_time = ngx_time(); sh->reset = 0; ngx_shmtx_unlock(&cache->shpool->mutex); for (cur_buffer = buffers; cur_buffer < last_buffer; cur_buffer++) { target_buffer = ngx_copy(target_buffer, cur_buffer->data, cur_buffer->len); } // Note: no need to obtain the lock since state is ngx_atomic_t entry->state = CES_READY; return 1; error: sh->stats.store_err++; sh->reset = 0; ngx_shmtx_unlock(&cache->shpool->mutex); return 0; }
ngx_int_t ngx_http_session_get_by_key(ngx_http_request_t *r) { size_t len; uint32_t hash; ngx_int_t rc; ngx_uint_t i, index; 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; ngx_http_session_store_variable_ctx_t *variable_ctx; sslcf = ngx_http_get_module_loc_conf(r, ngx_http_session_store_module); if (sslcf->get_shm_zone == NULL) { return NGX_DECLINED; } ctx = sslcf->get_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_DECLINED; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "find session: %*s", len, v->data); 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); if (ssn == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "not find session"); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; } ngx_queue_remove(&ssn->queue); ngx_queue_insert_head(&ctx->sh->queue, &ssn->queue); for (i = 0; i < 8; i++) { index = sslcf->get_index[i]; if (index == NGX_CONF_UNSET_UINT) { break; } vv = ngx_http_get_indexed_variable(r, index); if (vv == NULL) { break; } variable_ctx = ngx_http_get_module_ctx(r, ngx_http_session_store_module); if (variable_ctx == NULL) { variable_ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_session_store_variable_ctx_t)); if (variable_ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, variable_ctx, ngx_http_session_store_module); } variable_ctx->record[i].data = ssn->data + ssn->record[i].start; variable_ctx->record[i].len = ssn->record[i].length; vv->len = variable_ctx->record[i].len; vv->valid = 1; vv->no_cacheable = 0; vv->not_found = 0; vv->data = variable_ctx->record[i].data; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "value[%d] = %V", i, &variable_ctx->record[i]); } ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; }
/* * 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; }
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_limit_req_handler(ngx_http_request_t *r) { uint32_t hash; ngx_str_t key; ngx_int_t rc; ngx_uint_t n, excess; ngx_msec_t delay; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_conf_t *lrcf; ngx_http_limit_req_limit_t *limit, *limits; if (r->main->limit_req_set) { return NGX_DECLINED; } lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); limits = lrcf->limits.elts; excess = 0; rc = NGX_DECLINED; #if (NGX_SUPPRESS_WARN) limit = NULL; #endif for (n = 0; n < lrcf->limits.nelts; n++) { limit = &limits[n]; ctx = limit->shm_zone->data; if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (key.len == 0) { continue; } if (key.len > 65535) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the value of the \"%V\" key " "is more than 65535 bytes: \"%V\"", &ctx->key.value, &key); continue; } hash = ngx_crc32_short(key.data, key.len); ngx_shmtx_lock(&ctx->shpool->mutex); rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess, (n == lrcf->limits.nelts - 1)); ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit_req[%ui]: %i %ui.%03ui", n, rc, excess / 1000, excess % 1000); if (rc != NGX_AGAIN) { break; } } if (rc == NGX_DECLINED) { return NGX_DECLINED; } 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->shm_zone->shm.name); } while (n--) { ctx = limits[n].shm_zone->data; if (ctx->node == NULL) { continue; } ngx_shmtx_lock(&ctx->shpool->mutex); ctx->node->count--; ngx_shmtx_unlock(&ctx->shpool->mutex); ctx->node = NULL; } return lrcf->status_code; } /* rc == NGX_AGAIN || rc == NGX_OK */ if (rc == NGX_AGAIN) { excess = 0; } delay = ngx_http_limit_req_account(limits, n, &excess, &limit); if (!delay) { return NGX_DECLINED; } ngx_log_error(lrcf->delay_log_level, r->connection->log, 0, "delaying request, excess: %ui.%03ui, by zone \"%V\"", excess / 1000, excess % 1000, &limit->shm_zone->shm.name); 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_req_delay; r->connection->write->delayed = 1; ngx_add_timer(r->connection->write, delay); return NGX_AGAIN; }
static ngx_int_t ngx_http_limit_req_handler(ngx_http_request_t *r) { size_t len, n; uint32_t hash; ngx_int_t rc; ngx_uint_t excess; ngx_time_t *tp; ngx_rbtree_node_t *node; ngx_http_variable_value_t *vv; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lr; ngx_http_limit_req_conf_t *lrcf; if (r->main->limit_req_set) { return NGX_DECLINED; } lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); if (lrcf->shm_zone == NULL) { return NGX_DECLINED; } ctx = lrcf->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 > 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, vv); return NGX_DECLINED; } r->main->limit_req_set = 1; hash = ngx_crc32_short(vv->data, len); ngx_shmtx_lock(&ctx->shpool->mutex); ngx_http_limit_req_expire(ctx, 1); rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &lr); if (lr) { ngx_queue_remove(&lr->queue); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); excess = lr->excess; } else { excess = 0; } ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000); if (rc == NGX_BUSY) { ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "limiting requests, excess: %ui.%03ui by zone \"%V\"", excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name); return NGX_HTTP_SERVICE_UNAVAILABLE; } if (rc == NGX_AGAIN) { ngx_shmtx_unlock(&ctx->shpool->mutex); if (lrcf->nodelay) { return NGX_DECLINED; } ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "delaying request, excess: %ui.%03ui, by zone \"%V\"", excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name); 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_req_delay; ngx_add_timer(r->connection->write, (ngx_msec_t) excess); return NGX_AGAIN; } if (rc == NGX_OK) { goto done; } /* rc == NGX_DECLINED */ n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_req_node_t, data) + len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_http_limit_req_expire(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_req_node_t *) &node->color; node->key = hash; lr->len = (u_char) len; tp = ngx_timeofday(); lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); lr->excess = 0; ngx_memcpy(lr->data, vv->data, len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); done: ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_DECLINED; }
// 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_main_conf_t *mcf) { ngx_http_push_stream_shm_data_t *data = mcf->shm_data; ngx_http_push_stream_channel_t *channel; ngx_slab_pool_t *shpool = mcf->shpool; ngx_flag_t is_wildcard_channel = 0; if (id == NULL) { ngx_log_error(NGX_LOG_ERR, log, 0, "push stream module: tried to create a channel with a null id"); return NULL; } ngx_shmtx_lock(&data->channels_queue_mutex); // check again to see if any other worker didn't create the channel channel = ngx_http_push_stream_find_channel_on_tree(id, log, &data->tree); if (channel != NULL) { // we found our channel ngx_shmtx_unlock(&data->channels_queue_mutex); return channel; } if ((mcf->wildcard_channel_prefix.len > 0) && (ngx_strncmp(id->data, mcf->wildcard_channel_prefix.data, mcf->wildcard_channel_prefix.len) == 0)) { is_wildcard_channel = 1; } if (((!is_wildcard_channel) && (mcf->max_number_of_channels != NGX_CONF_UNSET_UINT) && (mcf->max_number_of_channels == data->channels)) || ((is_wildcard_channel) && (mcf->max_number_of_wildcard_channels != NGX_CONF_UNSET_UINT) && (mcf->max_number_of_wildcard_channels == data->wildcard_channels))) { ngx_shmtx_unlock(&data->channels_queue_mutex); ngx_log_error(NGX_LOG_ERR, log, 0, "push stream module: number of channels were exceeded"); return NGX_HTTP_PUSH_STREAM_NUMBER_OF_CHANNELS_EXCEEDED; } if ((channel = ngx_slab_alloc(shpool, sizeof(ngx_http_push_stream_channel_t))) == NULL) { ngx_shmtx_unlock(&data->channels_queue_mutex); ngx_log_error(NGX_LOG_ERR, log, 0, "push stream module: unable to allocate memory for new channel"); return NULL; } if ((channel->id.data = ngx_slab_alloc(shpool, id->len + 1)) == NULL) { ngx_slab_free(shpool, channel); ngx_shmtx_unlock(&data->channels_queue_mutex); ngx_log_error(NGX_LOG_ERR, log, 0, "push stream module: unable to allocate memory for new channel id"); 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->wildcard = is_wildcard_channel; channel->channel_deleted_message = NULL; channel->last_message_id = 0; channel->last_message_time = 0; channel->last_message_tag = 0; channel->stored_messages = 0; channel->subscribers = 0; channel->deleted = 0; channel->expires = ngx_time() + mcf->channel_inactivity_time; ngx_queue_init(&channel->message_queue); ngx_queue_init(&channel->workers_with_subscribers); channel->node.key = ngx_crc32_short(channel->id.data, channel->id.len); ngx_rbtree_insert(&data->tree, &channel->node); ngx_queue_insert_tail(&data->channels_queue, &channel->queue); (channel->wildcard) ? data->wildcard_channels++ : data->channels++; channel->mutex = &data->channels_mutex[data->mutex_round_robin++ % 9]; ngx_shmtx_unlock(&data->channels_queue_mutex); return channel; }
static ngx_int_t ngx_http_limit_traffic_rate_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { size_t len; uint32_t hash; ngx_int_t rc; ngx_uint_t delta_msec; ngx_msec_t delay = 0; ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node, *sentinel; ngx_http_variable_value_t *vv; ngx_http_limit_traffic_rate_filter_ctx_t *ctx; ngx_http_limit_traffic_rate_filter_node_t *lir; ngx_http_limit_traffic_rate_filter_conf_t *lircf; ngx_http_core_loc_conf_t *clcf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit traffic rate filter"); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); lircf = ngx_http_get_module_loc_conf(r, ngx_http_limit_traffic_rate_filter_module); if (lircf->shm_zone == NULL) { return ngx_http_next_body_filter(r, in); } ctx = lircf->shm_zone->data; vv = ngx_http_get_indexed_variable(r, ctx->index); if (vv == NULL || vv->not_found) { return ngx_http_next_body_filter(r, in); } len = vv->len; if (len == 0) { return ngx_http_next_body_filter(r, in); } if (len > 1024) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the value of the \"%V\" variable " "is more than 1024 bytes: \"%v\"", &ctx->var, vv); return ngx_http_next_body_filter(r, in); } hash = ngx_crc32_short(vv->data, len); shpool = (ngx_slab_pool_t *) lircf->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 { lir = (ngx_http_limit_traffic_rate_filter_node_t *) &node->color; rc = ngx_memn2cmp(vv->data, lir->data, len, (size_t) lir->len); if (rc == 0) { ngx_queue_t *p = lir->rq_top.next; ngx_http_limit_traffic_rate_filter_request_queue_t *tr; clcf->sendfile_max_chunk = lircf->limit_traffic_rate / lir->conn; for (; p;) { tr = ngx_queue_data(p, ngx_http_limit_traffic_rate_filter_request_queue_t, rq); if (tr->r == r) { tr->last_last_sent = tr->last_sent; tr->last_last_time = tr->last_time; tr->last_sent = r->connection->sent; tr->last_time = *ngx_cached_time; delta_msec = tr->last_time.sec * 1000 + tr->last_time.msec - tr->last_last_time.sec * 1000 - tr->last_last_time.msec; if (tr->last_sent - tr->last_last_sent > lircf->limit_traffic_rate/lir->conn) { /* first second */ delay = (ngx_msec_t)((tr->last_sent - tr->last_last_sent) *1000 / (lircf->limit_traffic_rate/lir->conn)); } else if (tr->last_time.sec == tr->last_last_time.sec && tr->last_time.msec == tr->last_last_time.msec) { /* first in, let download go */ break; } else if ((tr->last_sent - tr->last_last_sent)*1000 / delta_msec > lircf->limit_traffic_rate/lir->conn) { delay = (ngx_msec_t)((tr->last_sent - tr->last_last_sent) *1000 / (lircf->limit_traffic_rate/lir->conn)); } break; } if (ngx_queue_last(&lir->rq_top) == p) { break; } p = ngx_queue_next(p); } goto done; } node = (rc < 0) ? node->left : node->right; } while (node != sentinel && hash == node->key); break; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "rbtree search fail: %08XD %d", node->key, r->limit_rate); done: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit traffic rate: %08XD %d", node->key, delay); ngx_shmtx_unlock(&shpool->mutex); if (delay > 0) { r->connection->write->delayed = 1; ngx_add_timer(r->connection->write, delay); return NGX_OK; } return ngx_http_next_body_filter(r, in); }
static ngx_int_t ngx_http_limit_traffic_rate_filter_log_handler(ngx_http_request_t *r) { size_t len; uint32_t hash; ngx_int_t rc; ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node, *sentinel; ngx_http_variable_value_t *vv; ngx_http_limit_traffic_rate_filter_ctx_t *ctx; ngx_http_limit_traffic_rate_filter_node_t *lir; ngx_http_limit_traffic_rate_filter_conf_t *lircf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit traffic rate log phase handler"); lircf = ngx_http_get_module_loc_conf(r, ngx_http_limit_traffic_rate_filter_module); if (lircf->shm_zone == NULL) { return NGX_DECLINED; } ctx = lircf->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 > 1024) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the value of the \"%V\" variable " "is more than 1024 bytes: \"%v\"", &ctx->var, vv); return NGX_DECLINED; } hash = ngx_crc32_short(vv->data, len); shpool = (ngx_slab_pool_t *) lircf->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 { lir = (ngx_http_limit_traffic_rate_filter_node_t *) &node->color; rc = ngx_memn2cmp(vv->data, lir->data, len, (size_t) lir->len); if (rc == 0) { ngx_queue_t *p = lir->rq_top.next; ngx_http_limit_traffic_rate_filter_request_queue_t *tr; for (; p;) { tr = ngx_queue_data(p, ngx_http_limit_traffic_rate_filter_request_queue_t, rq); if (tr->r == r) { tr->r = NULL; ngx_queue_remove(p); ngx_slab_free_locked(shpool, tr); goto done; } if (ngx_queue_last(&lir->rq_top) == p) { break; } p = ngx_queue_next(p); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "queue search fail: %08XD", node->key); } node = (rc < 0) ? node->left : node->right; } while (node != sentinel && hash == node->key); break; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "rbtree search fail: %08XD %d", node->key, r->limit_rate); done: ngx_shmtx_unlock(&shpool->mutex); return NGX_DECLINED; }
static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) { uint32_t hash; in_addr_t addr, *addrs; ngx_int_t rc; ngx_uint_t naddrs; ngx_resolver_ctx_t *next; ngx_resolver_node_t *rn; hash = ngx_crc32_short(ctx->name.data, ctx->name.len); rn = ngx_resolver_lookup_name(r, &ctx->name, hash); if (rn) { if (rn->valid >= ngx_time()) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); ngx_queue_remove(&rn->queue); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); naddrs = rn->naddrs; if (naddrs) { /* NGX_RESOLVE_A answer */ if (naddrs != 1) { addr = 0; addrs = ngx_resolver_dup(r, rn->u.addrs, naddrs * sizeof(in_addr_t)); if (addrs == NULL) { return NGX_ERROR; } } else { addr = rn->u.addr; addrs = NULL; } ctx->next = rn->waiting; rn->waiting = NULL; /* unlock name mutex */ do { ctx->state = NGX_OK; ctx->naddrs = naddrs; ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs; ctx->addr = addr; next = ctx->next; ctx->handler(ctx); ctx = next; } while (ctx); if (addrs) { ngx_resolver_free(r, addrs); } return NGX_OK; } /* NGX_RESOLVE_CNAME */ if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) { ctx->name.len = rn->cnlen; ctx->name.data = rn->u.cname; return ngx_resolve_name_locked(r, ctx); } ctx->next = rn->waiting; rn->waiting = NULL; /* unlock name mutex */ do { ctx->state = NGX_RESOLVE_NXDOMAIN; next = ctx->next; ctx->handler(ctx); ctx = next; } while (ctx); return NGX_OK; } if (rn->waiting) { ctx->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; return NGX_AGAIN; } ngx_queue_remove(&rn->queue); /* lock alloc mutex */ ngx_resolver_free_locked(r, rn->query); rn->query = NULL; if (rn->cnlen) { ngx_resolver_free_locked(r, rn->u.cname); } if (rn->naddrs > 1) { ngx_resolver_free_locked(r, rn->u.addrs); } /* unlock alloc mutex */ } else { rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); if (rn == NULL) { return NGX_ERROR; } rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len); if (rn->name == NULL) { ngx_resolver_free(r, rn); return NGX_ERROR; } rn->node.key = hash; rn->nlen = (u_short) ctx->name.len; rn->query = NULL; ngx_rbtree_insert(&r->name_rbtree, &rn->node); } rc = ngx_resolver_create_name_query(rn, ctx); if (rc == NGX_ERROR) { goto failed; } if (rc == NGX_DECLINED) { ngx_rbtree_delete(&r->name_rbtree, &rn->node); ngx_resolver_free(r, rn->query); ngx_resolver_free(r, rn->name); ngx_resolver_free(r, rn); ctx->state = NGX_RESOLVE_NXDOMAIN; ctx->handler(ctx); return NGX_OK; } if (ngx_resolver_send_query(r, rn) != NGX_OK) { goto failed; } if (ctx->event == NULL) { ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); if (ctx->event == NULL) { goto failed; } ctx->event->handler = ngx_resolver_timeout_handler; ctx->event->data = ctx; ctx->event->log = r->log; ctx->ident = -1; ngx_add_timer(ctx->event, ctx->timeout); } if (ngx_queue_empty(&r->name_resend_queue)) { ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000)); } rn->expire = ngx_time() + r->resend_timeout; ngx_queue_insert_head(&r->name_resend_queue, &rn->queue); rn->cnlen = 0; rn->naddrs = 0; rn->valid = 0; rn->waiting = ctx; ctx->state = NGX_AGAIN; return NGX_AGAIN; failed: ngx_rbtree_delete(&r->name_rbtree, &rn->node); if (rn->query) { ngx_resolver_free(r, rn->query); } ngx_resolver_free(r, rn->name); ngx_resolver_free(r, rn); return NGX_ERROR; }
static ngx_int_t ngx_http_limit_traffic_rate_filter_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_traffic_rate_filter_ctx_t *ctx; ngx_http_limit_traffic_rate_filter_node_t *lir; ngx_http_limit_traffic_rate_filter_conf_t *lircf; ngx_http_limit_traffic_rate_filter_cleanup_t *lircln; ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); lircf = ngx_http_get_module_loc_conf(r, ngx_http_limit_traffic_rate_filter_module); if (lircf->shm_zone == NULL) { return NGX_DECLINED; } ctx = lircf->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 > 1024) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the value of the \"%V\" variable " "is more than 1024 bytes: \"%v\"", &ctx->var, vv); return NGX_DECLINED; } hash = ngx_crc32_short(vv->data, len); cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_traffic_rate_filter_cleanup_t)); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } shpool = (ngx_slab_pool_t *) lircf->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 { lir = (ngx_http_limit_traffic_rate_filter_node_t *) &node->color; rc = ngx_memn2cmp(vv->data, lir->data, len, (size_t) lir->len); if (rc == 0) { lir->conn++; ngx_http_limit_traffic_rate_filter_request_queue_t *req; req = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_limit_traffic_rate_filter_request_queue_t)); if (node == NULL) { ngx_shmtx_unlock(&shpool->mutex); return NGX_HTTP_SERVICE_UNAVAILABLE; } req->r = r; req->last_time = *ngx_cached_time; req->last_last_time = *ngx_cached_time; req->last_sent = 0; req->last_last_sent = 0; clcf->sendfile_max_chunk = lircf->limit_traffic_rate / lir->conn; ngx_queue_insert_tail(&(lir->rq_top), &req->rq); 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_traffic_rate_filter_node_t, data) + len; node = ngx_slab_alloc_locked(shpool, n); if (node == NULL) { ngx_shmtx_unlock(&shpool->mutex); return NGX_HTTP_SERVICE_UNAVAILABLE; } lir = (ngx_http_limit_traffic_rate_filter_node_t *) &node->color; node->key = hash; lir->len = (u_short) len; lir->conn = 1; lir->start_sec = r->start_sec; ngx_queue_init(&(lir->rq_top)); ngx_http_limit_traffic_rate_filter_request_queue_t *req; req = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_limit_traffic_rate_filter_request_queue_t)); if (req == NULL) { ngx_shmtx_unlock(&shpool->mutex); return NGX_HTTP_SERVICE_UNAVAILABLE; } req->r = r; req->last_time = *ngx_cached_time; req->last_last_time = *ngx_cached_time; req->last_sent = 0; req->last_last_sent = 0; clcf->sendfile_max_chunk = lircf->limit_traffic_rate / lir->conn; ngx_queue_insert_tail(&(lir->rq_top), &req->rq); ngx_memcpy(lir->data, vv->data, len); ngx_rbtree_insert(ctx->rbtree, node); done: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit traffic rate: %08XD %d", node->key, lir->conn); ngx_shmtx_unlock(&shpool->mutex); cln->handler = ngx_http_limit_traffic_rate_filter_cleanup; lircln = cln->data; lircln->shm_zone = lircf->shm_zone; lircln->node = node; return NGX_DECLINED; }
void ngx_resolve_name_done(ngx_resolver_ctx_t *ctx) { uint32_t hash; ngx_resolver_t *r; ngx_resolver_ctx_t *w, **p; ngx_resolver_node_t *rn; r = ctx->resolver; ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve name done: %i", ctx->state); if (ctx->quick) { return; } if (ctx->event && ctx->event->timer_set) { ngx_del_timer(ctx->event); } /* lock name mutex */ if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { hash = ngx_crc32_short(ctx->name.data, ctx->name.len); rn = ngx_resolver_lookup_name(r, &ctx->name, hash); if (rn) { p = &rn->waiting; w = rn->waiting; while (w) { if (w == ctx) { *p = w->next; goto done; } p = &w->next; w = w->next; } } ngx_log_error(NGX_LOG_ALERT, r->log, 0, "could not cancel %V resolving", &ctx->name); } done: ngx_resolver_expire(r, &r->name_rbtree, &r->name_expire_queue); /* unlock name mutex */ /* lock alloc mutex */ if (ctx->event) { ngx_resolver_free_locked(r, ctx->event); } ngx_resolver_free_locked(r, ctx); /* unlock alloc mutex */ }
static void ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans) { char *err; u_char *cname; size_t len; int32_t ttl; uint32_t hash; in_addr_t addr, *addrs; ngx_str_t name; ngx_uint_t qtype, qident, naddrs, a, i, n, start; ngx_resolver_an_t *an; ngx_resolver_ctx_t *ctx, *next; ngx_resolver_node_t *rn; if (ngx_resolver_copy(r, &name, buf, &buf[12], &buf[last]) != NGX_OK) { return; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qs:%V", &name); hash = ngx_crc32_short(name.data, name.len); /* lock name mutex */ rn = ngx_resolver_lookup_name(r, &name, hash); if (rn == NULL || rn->query == NULL) { ngx_log_error(r->log_level, r->log, 0, "unexpected response for %V", &name); goto failed; } qident = (rn->query[0] << 8) + rn->query[1]; if (ident != qident) { ngx_log_error(r->log_level, r->log, 0, "wrong ident %ui response for %V, expect %ui", ident, &name, qident); goto failed; } ngx_resolver_free(r, name.data); if (code == 0 && nan == 0) { code = 3; /* NXDOMAIN */ } if (code) { next = rn->waiting; rn->waiting = NULL; ngx_queue_remove(&rn->queue); ngx_rbtree_delete(&r->name_rbtree, &rn->node); ngx_resolver_free_node(r, rn); /* unlock name mutex */ while (next) { ctx = next; ctx->state = code; next = ctx->next; ctx->handler(ctx); } return; } i = ans; naddrs = 0; addr = 0; addrs = NULL; cname = NULL; qtype = 0; ttl = 0; for (a = 0; a < nan; a++) { start = i; while (i < last) { if (buf[i] & 0xc0) { i += 2; goto found; } if (buf[i] == 0) { i++; goto test_length; } i += 1 + buf[i]; } goto short_response; test_length: if (i - start < 2) { err = "invalid name in dns response"; goto invalid; } found: if (i + sizeof(ngx_resolver_an_t) >= last) { goto short_response; } an = (ngx_resolver_an_t *) &buf[i]; qtype = (an->type_hi << 8) + an->type_lo; len = (an->len_hi << 8) + an->len_lo; ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16) + (an->ttl[2] << 8) + (an->ttl[3]); if (ttl < 0) { ttl = 0; } if (qtype == NGX_RESOLVE_A) { i += sizeof(ngx_resolver_an_t); if (i + len > last) { goto short_response; } addr = htonl((buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + (buf[i + 3])); naddrs++; i += len; } else if (qtype == NGX_RESOLVE_CNAME) { cname = &buf[i] + sizeof(ngx_resolver_an_t); i += sizeof(ngx_resolver_an_t) + len; } else if (qtype == NGX_RESOLVE_DNAME) { i += sizeof(ngx_resolver_an_t) + len; } else { ngx_log_error(r->log_level, r->log, 0, "unexpected qtype %ui", qtype); } } ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver naddrs:%ui cname:%p ttl:%d", naddrs, cname, ttl); if (naddrs) { if (naddrs == 1) { rn->u.addr = addr; } else { addrs = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t)); if (addrs == NULL) { return; } n = 0; i = ans; for (a = 0; a < nan; a++) { for ( ;; ) { if (buf[i] & 0xc0) { i += 2; goto ok; } if (buf[i] == 0) { i++; goto ok; } i += 1 + buf[i]; } ok: an = (ngx_resolver_an_t *) &buf[i]; qtype = (an->type_hi << 8) + an->type_lo; len = (an->len_hi << 8) + an->len_lo; i += sizeof(ngx_resolver_an_t); if (qtype == NGX_RESOLVE_A) { addrs[n++] = htonl((buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + (buf[i + 3])); if (n == naddrs) { break; } } i += len; } rn->u.addrs = addrs; addrs = ngx_resolver_dup(r, rn->u.addrs, naddrs * sizeof(in_addr_t)); if (addrs == NULL) { return; } } rn->naddrs = (u_short) naddrs; ngx_queue_remove(&rn->queue); rn->valid = ngx_time() + (r->valid ? r->valid : ttl); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); next = rn->waiting; rn->waiting = NULL; /* unlock name mutex */ while (next) { ctx = next; ctx->state = NGX_OK; ctx->naddrs = naddrs; ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs; ctx->addr = addr; next = ctx->next; ctx->handler(ctx); } if (naddrs > 1) { ngx_resolver_free(r, addrs); } return; } else if (cname) { /* CNAME only */ if (ngx_resolver_copy(r, &name, buf, cname, &buf[last]) != NGX_OK) { return; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver cname:\"%V\"", &name); ngx_queue_remove(&rn->queue); rn->cnlen = (u_short) name.len; rn->u.cname = name.data; rn->valid = ngx_time() + (r->valid ? r->valid : ttl); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); ctx = rn->waiting; rn->waiting = NULL; if (ctx) { ctx->name = name; (void) ngx_resolve_name_locked(r, ctx); } return; } ngx_log_error(r->log_level, r->log, 0, "no A or CNAME types in DNS responses, unknown query type: %ui", qtype); return; short_response: err = "short dns response"; invalid: /* unlock name mutex */ ngx_log_error(r->log_level, r->log, 0, err); return; failed: /* unlock name mutex */ ngx_resolver_free(r, name.data); return; }
static void ngx_http_upstream_dynamic_server_resolve_handler(ngx_resolver_ctx_t *ctx) { ngx_http_upstream_dynamic_server_main_conf_t *udsmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_upstream_dynamic_servers_module); ngx_http_upstream_dynamic_server_conf_t *dynamic_server; ngx_conf_t cf; uint32_t hash; ngx_resolver_node_t *rn; ngx_pool_t *new_pool; ngx_addr_t *addrs; dynamic_server = ctx->data; ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: Finished resolving '%V'", &ctx->name); if (ctx->state) { ngx_log_error(NGX_LOG_ERR, ctx->resolver->log, 0, "upstream-dynamic-servers: '%V' could not be resolved (%i: %s)", &ctx->name, ctx->state, ngx_resolver_strerror(ctx->state)); ngx_url_t u; ngx_memzero(&u, sizeof(ngx_url_t)); // If the domain fails to resolve on start up, assign a static IP that // should never route (we'll also mark it as down in the upstream later // on). This is to account for various things inside nginx that seem to // expect a server to always have at least 1 IP. u.url = ngx_http_upstream_dynamic_server_null_route; u.default_port = 80; u.no_resolve = 1; if (ngx_parse_url(ngx_cycle->pool, &u) != NGX_OK) { if (u.err) { ngx_log_error(NGX_LOG_ERR, ctx->resolver->log, 0, "%s in upstream \"%V\"", u.err, &u.url); } goto end; } ctx->addr.sockaddr = u.addrs[0].sockaddr; ctx->addr.socklen = u.addrs[0].socklen; ctx->addr.name = u.addrs[0].name; ctx->addrs = &ctx->addr; ctx->naddrs = u.naddrs; } if (ctx->naddrs != dynamic_server->server->naddrs) { goto reinit_upstream; } ngx_uint_t i, j, founded; ngx_addr_t *existing_addr; for (i = 0; i < ctx->naddrs; i++) { founded = 0; for (j = 0; j < ctx->naddrs; j++) { existing_addr = &dynamic_server->server->addrs[j]; if (ngx_cmp_sockaddr(existing_addr->sockaddr, existing_addr->socklen, ctx->addrs[i].sockaddr, ctx->addrs[i].socklen, 0) == NGX_OK) { founded = 1; break; } } if (!founded) { goto reinit_upstream; } } ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: No DNS changes for '%V' - keeping existing upstream configuration", &ctx->name); goto end; reinit_upstream: new_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, ctx->resolver->log); if (new_pool == NULL) { ngx_log_error(NGX_LOG_ERR, ctx->resolver->log, 0, "upstream-dynamic-servers: Could not create new pool"); goto end; } ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: DNS changes for '%V' detected - reinitializing upstream configuration", &ctx->name); ngx_memzero(&cf, sizeof(ngx_conf_t)); cf.name = "dynamic_server_init_upstream"; cf.cycle = (ngx_cycle_t *) ngx_cycle; cf.pool = new_pool; cf.module_type = NGX_HTTP_MODULE; cf.cmd_type = NGX_HTTP_MAIN_CONF; cf.log = ngx_cycle->log; cf.ctx = udsmcf->conf_ctx; addrs = ngx_pcalloc(new_pool, ctx->naddrs * sizeof(ngx_addr_t)); ngx_memcpy(addrs, ctx->addrs, ctx->naddrs * sizeof(ngx_addr_t)); struct sockaddr *sockaddr; ngx_addr_t *addr; socklen_t socklen; for (i = 0; i < ctx->naddrs; i++) { addr = &addrs[i]; socklen = ctx->addrs[i].socklen; sockaddr = ngx_palloc(new_pool, socklen); ngx_memcpy(sockaddr, ctx->addrs[i].sockaddr, socklen); switch(sockaddr->sa_family) { case AF_INET6: ((struct sockaddr_in6 *)sockaddr)->sin6_port = htons((u_short) dynamic_server->port); break; default: ((struct sockaddr_in *)sockaddr)->sin_port = htons((u_short) dynamic_server->port); } addr->sockaddr = sockaddr; addr->socklen = socklen; u_char *p; size_t len; p = ngx_pnalloc(new_pool, NGX_SOCKADDR_STRLEN); if (p == NULL) { ngx_log_error(NGX_LOG_ERR, ctx->resolver->log, 0, "upstream-dynamic-servers: Error initializing sockaddr"); ngx_destroy_pool(new_pool); goto end; } len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); addr->name.len = len; addr->name.data = p; ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: '%V' was resolved to '%V'", &ctx->name, &addr->name); } // If the domain failed to resolve, mark this server as down. dynamic_server->server->down = ctx->state ? 1 : 0; dynamic_server->server->addrs = addrs; dynamic_server->server->naddrs = ctx->naddrs; ngx_http_upstream_init_pt init; init = dynamic_server->upstream_conf->peer.init_upstream ? dynamic_server->upstream_conf->peer.init_upstream : ngx_http_upstream_init_round_robin; if (init(&cf, dynamic_server->upstream_conf) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, ctx->resolver->log, 0, "upstream-dynamic-servers: Error re-initializing upstream after DNS changes"); } if (dynamic_server->previous_pool != NULL) { ngx_destroy_pool(dynamic_server->previous_pool); dynamic_server->previous_pool = NULL; } dynamic_server->previous_pool = dynamic_server->pool; dynamic_server->pool = new_pool; end: if (ctx->resolver->log->log_level & NGX_LOG_DEBUG_CORE) { hash = ngx_crc32_short(ctx->name.data, ctx->name.len); rn = ngx_resolver_lookup_name(ctx->resolver, &ctx->name, hash); uint32_t refresh_in; if (rn != NULL && rn->ttl) { refresh_in = (rn->valid - ngx_time()) * 1000; if (!refresh_in || refresh_in < 1000) { refresh_in = 1000; } } else { refresh_in = 1000; } ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: Refreshing DNS of '%V' in %ims", &ctx->name, refresh_in); } ngx_resolve_name_done(ctx); if (ngx_exiting) { ngx_log_debug(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "upstream-dynamic-servers: worker is about to exit, do not set the timer again"); return; } ngx_add_timer(&dynamic_server->timer, 1000); }
static int ngx_http_lua_shdict_incr(lua_State *L) { int n; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; lua_Number num; u_char *p; ngx_shm_zone_t *zone; lua_Number value; n = lua_gettop(L); if (n != 3) { return luaL_error(L, "expecting 3 arguments, " "but only seen %d", n); } luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); } ctx = zone->data; key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { return luaL_error(L, "attemp to use empty keys"); } if (key.len > 65535) { return luaL_error(L, "the key argument is more than 65535 bytes: %d", (int) key.len); } hash = ngx_crc32_short(key.data, key.len); value = luaL_checknumber(L, 3); dd("looking up key %.*s in shared dict %.*s", (int) key.len, key.data, (int) ctx->name.len, ctx->name.data); ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returned %d", (int) rc); if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnil(L); lua_pushliteral(L, "not found"); return 2; } /* rc == NGX_OK */ if (sd->value_type != LUA_TNUMBER || sd->value_len != sizeof(lua_Number)) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnil(L); lua_pushliteral(L, "not a number"); return 2; } ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); dd("setting value type to %d", (int) sd->value_type); p = sd->data + key.len; num = *(lua_Number *) p; num += value; ngx_memcpy(p, (lua_Number *) &num, sizeof(lua_Number)); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnumber(L, num); lua_pushnil(L); return 2; }
static int ngx_http_lua_shdict_get_helper(lua_State *L, int get_stale) { int n; ngx_str_t name; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; ngx_str_t value; int value_type; double num; u_char c; ngx_shm_zone_t *zone; uint32_t user_flags = 0; n = lua_gettop(L); if (n != 2) { return luaL_error(L, "expecting exactly two arguments, " "but only seen %d", n); } zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad \"zone\" argument"); } ctx = zone->data; name = ctx->name; 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); #if (NGX_DEBUG) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "fetching key \"%V\" in shared dict \"%V\"", &key, &name); #endif /* NGX_DEBUG */ ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 if (!get_stale) { ngx_http_lua_shdict_expire(ctx, 1); } #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returns %d", (int) rc); if (rc == NGX_DECLINED || (rc == NGX_DONE && !get_stale)) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnil(L); return 1; } /* rc == NGX_OK || (rc == NGX_DONE && get_stale) */ value_type = sd->value_type; dd("data: %p", sd->data); dd("key len: %d", (int) sd->key_len); value.data = sd->data + sd->key_len; value.len = (size_t) sd->value_len; switch (value_type) { case LUA_TSTRING: lua_pushlstring(L, (char *) value.data, value.len); break; case LUA_TNUMBER: if (value.len != sizeof(double)) { ngx_shmtx_unlock(&ctx->shpool->mutex); return luaL_error(L, "bad lua number value size found for key %s " "in shared_dict %s: %lu", key.data, name.data, (unsigned long) value.len); } num = *(double *) value.data; lua_pushnumber(L, num); break; case LUA_TBOOLEAN: if (value.len != sizeof(u_char)) { ngx_shmtx_unlock(&ctx->shpool->mutex); return luaL_error(L, "bad lua boolean value size found for key %s " "in shared_dict %s: %lu", key.data, name.data, (unsigned long) value.len); } c = *value.data; lua_pushboolean(L, c ? 1 : 0); break; default: ngx_shmtx_unlock(&ctx->shpool->mutex); return luaL_error(L, "bad value type found for key %s in " "shared_dict %s: %d", key.data, name.data, value_type); } user_flags = sd->user_flags; ngx_shmtx_unlock(&ctx->shpool->mutex); if (get_stale) { /* always return value, flags, stale */ if (user_flags) { lua_pushinteger(L, (lua_Integer) user_flags); } else { lua_pushnil(L); } lua_pushboolean(L, rc == NGX_DONE); return 3; } if (user_flags) { lua_pushinteger(L, (lua_Integer) user_flags); return 2; } return 1; }
ngx_int_t ngx_http_vhost_traffic_status_filter_unique(ngx_pool_t *pool, ngx_array_t **keys) { uint32_t hash; u_char *p; ngx_str_t key; ngx_uint_t i, n; ngx_array_t *uniqs, *filter_keys; ngx_http_vhost_traffic_status_filter_t *filter, *filters; ngx_http_vhost_traffic_status_filter_uniq_t *filter_uniqs; if (*keys == NULL) { return NGX_OK; } uniqs = ngx_array_create(pool, 1, sizeof(ngx_http_vhost_traffic_status_filter_uniq_t)); if (uniqs == NULL) { return NGX_ERROR; } /* init array */ filter_keys = NULL; filter_uniqs = NULL; filters = (*keys)->elts; n = (*keys)->nelts; for (i = 0; i < n; i++) { key.len = filters[i].filter_key.value.len + filters[i].filter_name.value.len; key.data = ngx_pcalloc(pool, key.len); if (key.data == NULL) { return NGX_ERROR; } p = key.data; p = ngx_cpymem(p, filters[i].filter_key.value.data, filters[i].filter_key.value.len); ngx_memcpy(p, filters[i].filter_name.value.data, filters[i].filter_name.value.len); hash = ngx_crc32_short(key.data, key.len); filter_uniqs = ngx_array_push(uniqs); if (filter_uniqs == NULL) { return NGX_ERROR; } filter_uniqs->hash = hash; filter_uniqs->index = i; if (p != NULL) { ngx_pfree(pool, key.data); } } filter_uniqs = uniqs->elts; n = uniqs->nelts; ngx_qsort(filter_uniqs, (size_t) n, sizeof(ngx_http_vhost_traffic_status_filter_uniq_t), ngx_http_traffic_status_filter_cmp_hashs); hash = 0; for (i = 0; i < n; i++) { if (filter_uniqs[i].hash == hash) { continue; } hash = filter_uniqs[i].hash; if (filter_keys == NULL) { filter_keys = ngx_array_create(pool, 1, sizeof(ngx_http_vhost_traffic_status_filter_t)); if (filter_keys == NULL) { return NGX_ERROR; } } filter = ngx_array_push(filter_keys); if (filter == NULL) { return NGX_ERROR; } ngx_memcpy(filter, &filters[filter_uniqs[i].index], sizeof(ngx_http_vhost_traffic_status_filter_t)); } if ((*keys)->nelts != filter_keys->nelts) { *keys = filter_keys; } return NGX_OK; }
static int ngx_http_lua_shdict_set_helper(lua_State *L, int flags) { int i, n; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_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 *) # 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_HTTP_LUA_SHDICT_ADD|NGX_HTTP_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_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returned %d", (int) rc); if (flags & NGX_HTTP_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_HTTP_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_HTTP, 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_HTTP, 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_HTTP, ctx->log, 0, "lua shared dict set: creating a new entry"); n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_lua_shdict_node_t, data) + key.len + value.len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { if (flags & NGX_HTTP_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_HTTP, 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_http_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_http_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_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; }
ngx_int_t ngx_http_lua_shared_dict_get(ngx_shm_zone_t *zone, u_char *key_data, size_t key_len, ngx_http_lua_value_t *value) { u_char *data; size_t len; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; if (zone == NULL) { return NGX_ERROR; } hash = ngx_crc32_short(key_data, key_len); ctx = zone->data; ngx_shmtx_lock(&ctx->shpool->mutex); rc = ngx_http_lua_shdict_lookup(zone, hash, key_data, key_len, &sd); dd("shdict lookup returned %d", (int) rc); if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); return rc; } /* rc == NGX_OK */ value->type = sd->value_type; dd("type: %d", (int) value->type); data = sd->data + sd->key_len; len = (size_t) sd->value_len; switch (value->type) { case LUA_TSTRING: if (value->value.s.data == NULL || value->value.s.len == 0) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "no string buffer " "initialized"); return NGX_ERROR; } if (len > value->value.s.len) { len = value->value.s.len; } else { value->value.s.len = len; } ngx_memcpy(value->value.s.data, data, len); break; case LUA_TNUMBER: if (len != sizeof(double)) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua number " "value size found for key %*s: %lu", key_len, key_data, (unsigned long) len); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_ERROR; } ngx_memcpy(&value->value.b, data, len); break; case LUA_TBOOLEAN: if (len != sizeof(u_char)) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua boolean " "value size found for key %*s: %lu", key_len, key_data, (unsigned long) len); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_ERROR; } value->value.b = *data; break; default: ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua value type " "found for key %*s: %d", key_len, key_data, (int) value->type); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_ERROR; } ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; }
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; }
int ngx_http_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_http_lua_shdict_ctx_t *ctx; ngx_http_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_HTTP_LUA_SHDICT_ADD|NGX_HTTP_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_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd); dd("lookup returns %d", (int) rc); if (op & NGX_HTTP_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_HTTP_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_HTTP, 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_HTTP, 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_HTTP, ctx->log, 0, "lua shared dict set: creating a new entry"); n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_lua_shdict_node_t, data) + key_len + str_value_len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { if (op & NGX_HTTP_LUA_SHDICT_SAFE_STORE) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "no memory"; return NGX_ERROR; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, 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_http_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_http_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; }
static uint32_t rbtree_hash_crc32(void *str) { return ngx_crc32_short(((ngx_str_t *)str)->data, ((ngx_str_t *)str)->len); }
int ngx_http_lua_ffi_shdict_get(ngx_shm_zone_t *zone, u_char *key, size_t key_len, int *value_type, u_char **str_value_buf, size_t *str_value_len, double *num_value, int *user_flags, int get_stale, int *is_stale) { ngx_str_t name; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; ngx_str_t value; if (zone == NULL) { return NGX_ERROR; } ctx = zone->data; name = ctx->name; hash = ngx_crc32_short(key, key_len); #if (NGX_DEBUG) ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "fetching key \"%*s\" in shared dict \"%V\"", key_len, key, &name); #endif /* NGX_DEBUG */ ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 if (!get_stale) { ngx_http_lua_shdict_expire(ctx, 1); } #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd); dd("shdict lookup returns %d", (int) rc); if (rc == NGX_DECLINED || (rc == NGX_DONE && !get_stale)) { ngx_shmtx_unlock(&ctx->shpool->mutex); *value_type = LUA_TNIL; return NGX_OK; } /* rc == NGX_OK || (rc == NGX_DONE && get_stale) */ *value_type = sd->value_type; dd("data: %p", sd->data); dd("key len: %d", (int) sd->key_len); value.data = sd->data + sd->key_len; value.len = (size_t) sd->value_len; if (*str_value_len < (size_t) value.len) { if (*value_type != LUA_TSTRING) { return NGX_ERROR; } *str_value_buf = malloc(value.len); if (*str_value_buf == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_ERROR; } } *str_value_len = value.len; switch (*value_type) { case LUA_TSTRING: ngx_memcpy(*str_value_buf, value.data, value.len); break; case LUA_TNUMBER: if (value.len != sizeof(double)) { ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua number value size found for key %*s " "in shared_dict %V: %z", key_len, key, &name, value.len); return NGX_ERROR; } *num_value = *(double *) value.data; break; case LUA_TBOOLEAN: if (value.len != sizeof(u_char)) { ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua boolean value size found for key %*s " "in shared_dict %V: %z", key_len, key, &name, value.len); return NGX_ERROR; } ngx_memcpy(*str_value_buf, value.data, value.len); break; default: ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad value type found for key %*s in " "shared_dict %V: %d", key_len, key, &name, *value_type); } *user_flags = sd->user_flags; dd("user flags: %d", *user_flags); ngx_shmtx_unlock(&ctx->shpool->mutex); if (get_stale) { /* always return value, flags, stale */ *is_stale = (rc == NGX_DONE); return NGX_OK; } return NGX_OK; }
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 ngx_int_t ngx_http_limit_conn_handler(ngx_http_request_t *r) { syslog(LOG_INFO, "[%s:%s:%d]", __FILE__, __func__, __LINE__); size_t len, n; uint32_t hash; ngx_uint_t i; ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node; ngx_pool_cleanup_t *cln; ngx_http_variable_value_t *vv; ngx_http_limit_conn_ctx_t *ctx; ngx_http_limit_conn_node_t *lc; ngx_http_limit_conn_conf_t *lccf; ngx_http_limit_conn_limit_t *limits; ngx_http_limit_conn_cleanup_t *lccln; if (r->main->limit_conn_set) { return NGX_DECLINED; } lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module); limits = lccf->limits.elts; for (i = 0; i < lccf->limits.nelts; i++) { ctx = limits[i].shm_zone->data; vv = ngx_http_get_indexed_variable(r, ctx->index); if (vv == NULL || vv->not_found) { continue; } len = vv->len; if (len == 0) { continue; } 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); continue; } r->main->limit_conn_set = 1; hash = ngx_crc32_short(vv->data, len); shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr; ngx_shmtx_lock(&shpool->mutex); node = ngx_http_limit_conn_lookup(ctx->rbtree, vv, hash); if (node == NULL) { n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_conn_node_t, data) + len; node = ngx_slab_alloc_locked(shpool, n); if (node == NULL) { ngx_shmtx_unlock(&shpool->mutex); ngx_http_limit_conn_cleanup_all(r->pool); return NGX_HTTP_SERVICE_UNAVAILABLE; } lc = (ngx_http_limit_conn_node_t *) &node->color; node->key = hash; lc->len = (u_char) len; lc->conn = 1; ngx_memcpy(lc->data, vv->data, len); ngx_rbtree_insert(ctx->rbtree, node); } else { lc = (ngx_http_limit_conn_node_t *) &node->color; if ((ngx_uint_t) lc->conn >= limits[i].conn) { ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(lccf->log_level, r->connection->log, 0, "limiting connections by zone \"%V\"", &limits[i].shm_zone->shm.name); ngx_http_limit_conn_cleanup_all(r->pool); return NGX_HTTP_SERVICE_UNAVAILABLE; } lc->conn++; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit conn: %08XD %d", node->key, lc->conn); ngx_shmtx_unlock(&shpool->mutex); cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_conn_cleanup_t)); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cln->handler = ngx_http_limit_conn_cleanup; lccln = cln->data; lccln->shm_zone = limits[i].shm_zone; lccln->node = node; } return NGX_DECLINED; }
static ngx_int_t ngx_http_ip_blacklist_handler(ngx_http_request_t *r) { ngx_http_ip_blacklist_main_conf_t *imcf; ngx_http_ip_blacklist_t *node; ngx_http_ip_blacklist_tree_t *blacklist; uint32_t hash; ngx_array_t *xfwd; ngx_table_elt_t **h; ngx_str_t src_addr_text; ngx_int_t i; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip blacklist handler begin"); imcf = ngx_http_get_module_main_conf(r, ngx_http_ip_blacklist_module); if (!imcf->enabled) { return NGX_DECLINED; } #if (NGX_HTTP_X_FORWARDED_FOR) if (r->headers_in.x_forwarded_for.nelts > 0) { xfwd = &r->headers_in.x_forwarded_for; h = xfwd->elts; src_addr_text = h[0]->value; } else #endif { src_addr_text = r->connection->addr_text; } hash = ngx_crc32_short(src_addr_text.data, src_addr_text.len); blacklist = ngx_http_ip_blacklist_shm_zone->data; ngx_shmtx_lock(&blacklist->shpool->mutex); node = ngx_http_ip_blacklist_lookup(&blacklist->blacklist, &src_addr_text, hash); if (node == NULL) { ngx_shmtx_unlock(&blacklist->shpool->mutex); return NGX_DECLINED; } if (!node->blacklist) { 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; } } /* avoid being destroyed by manager */ r->ip_blacklist_node = node; node->ref++; ngx_http_ip_blacklist_request_cleanup_init(r); ngx_shmtx_unlock(&blacklist->shpool->mutex); return NGX_DECLINED; } /* deny this request */ ngx_shmtx_unlock(&blacklist->shpool->mutex); return NGX_ERROR; }
static int ngx_http_lua_shdict_set_helper(lua_State *L, int flags) { #if (NGX_DEBUG) ngx_http_request_t *r; #endif int i, n; ngx_str_t name; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; ngx_str_t value; int value_type; lua_Number 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); } luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); } ctx = zone->data; name = ctx->name; #if (NGX_DEBUG) lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); r = lua_touserdata(L, -1); lua_pop(L, 1); #endif key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { return luaL_error(L, "attemp to use empty keys"); } if (key.len > 65535) { return luaL_error(L, "the key argument is more than 65535 bytes: %d", (int) key.len); } 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(lua_Number); num = lua_tonumber(L, 3); value.data = (u_char *) # 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_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) { return luaL_error(L, "attempt to add or replace nil values"); } value.len = 0; value.data = NULL; break; default: return luaL_error(L, "unsupported value type for key \"%s\" in " "shared_dict \"%s\": %s", key.data, name.data, lua_typename(L, value_type)); } if (n == 4) { exptime = luaL_checknumber(L, 4); if (exptime < 0) { exptime = 0; } } if (n == 5) { user_flags = (uint32_t) luaL_checkinteger(L, 5); } dd("looking up key %s in shared dict %s", key.data, name.data); ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returned %d", (int) rc); if (flags & NGX_HTTP_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_HTTP_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_HTTP, r->connection->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 = key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (ngx_msec_t) (tp->sec * 1000 + tp->msec) + (ngx_msec_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 = 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_HTTP, r->connection->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_HTTP, r->connection->log, 0, "lua shared dict set: creating a new entry"); n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_lua_shdict_node_t, data) + key.len + value.len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua shared dict set: overriding non-expired items due to memory " "shortage for entry \"%V\"", &name); for (i = 0; i < 30; i++) { if (ngx_http_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_http_lua_shdict_node_t *) &node->color; node->key = hash; sd->key_len = key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (ngx_msec_t) (tp->sec * 1000 + tp->msec) + (ngx_msec_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 = 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 int ngx_http_lua_shdict_get(lua_State *L) { int n; ngx_str_t name; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; ngx_str_t value; int value_type; lua_Number num; u_char c; ngx_shm_zone_t *zone; uint32_t user_flags = 0; n = lua_gettop(L); if (n != 2) { return luaL_error(L, "expecting exactly two arguments, " "but only seen %d", n); } luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); } ctx = zone->data; name = ctx->name; key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { lua_pushnil(L); return 1; } if (key.len > 65535) { return luaL_error(L, "the key argument is more than 65535 bytes: \"%s\"", key.data); } hash = ngx_crc32_short(key.data, key.len); #if (NGX_DEBUG) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "fetching key \"%V\" in shared dict \"%V\"", &key, &name); #endif /* NGX_DEBUG */ ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returns %d", (int) rc); if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnil(L); return 1; } /* rc == NGX_OK */ value_type = sd->value_type; dd("data: %p", sd->data); dd("key len: %d", (int) sd->key_len); value.data = sd->data + sd->key_len; value.len = (size_t) sd->value_len; switch (value_type) { case LUA_TSTRING: lua_pushlstring(L, (char *) value.data, value.len); break; case LUA_TNUMBER: if (value.len != sizeof(lua_Number)) { ngx_shmtx_unlock(&ctx->shpool->mutex); return luaL_error(L, "bad lua number value size found for key %s " "in shared_dict %s: %lu", key.data, name.data, (unsigned long) value.len); } num = *(lua_Number *) value.data; lua_pushnumber(L, num); break; case LUA_TBOOLEAN: if (value.len != sizeof(u_char)) { ngx_shmtx_unlock(&ctx->shpool->mutex); return luaL_error(L, "bad lua boolean value size found for key %s " "in shared_dict %s: %lu", key.data, name.data, (unsigned long) value.len); } c = *value.data; lua_pushboolean(L, c ? 1 : 0); break; default: ngx_shmtx_unlock(&ctx->shpool->mutex); return luaL_error(L, "bad value type found for key %s in " "shared_dict %s: %d", key.data, name.data, value_type); } user_flags = sd->user_flags; ngx_shmtx_unlock(&ctx->shpool->mutex); if (user_flags) { lua_pushinteger(L, (lua_Integer) user_flags); return 2; } return 1; }
static uint32_t ngx_http_cloudrouter_hash_route(hs_route_t *route) { return ngx_crc32_short(route->di_name, route->di_nlen); }