static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) { ngx_http_file_cache_node_t *fcn; ngx_shmtx_lock(&cache->shpool->mutex); fcn = ngx_http_file_cache_lookup(cache, c->key); if (fcn == NULL) { fcn = ngx_slab_calloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { ngx_shmtx_unlock(&cache->shpool->mutex); return NGX_ERROR; } ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t)); ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)], NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node); fcn->uses = 1; fcn->exists = 1; fcn->fs_size = c->fs_size; cache->sh->size += c->fs_size; } else { ngx_queue_remove(&fcn->queue); } fcn->expire = ngx_time() + cache->inactive; ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); ngx_shmtx_unlock(&cache->shpool->mutex); return NGX_OK; }
static void ngx_http_push_stream_initialize_channel(ngx_http_push_stream_channel_t *channel) { ngx_http_push_stream_shm_data_t *data = (ngx_http_push_stream_shm_data_t *) ngx_http_push_stream_shm_zone->data; 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 = 0; channel->last_activity_time = ngx_time(); ngx_queue_init(&channel->message_queue); 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->queue_sentinel = &data->channels_queue; (channel->broadcast) ? data->broadcast_channels++ : data->channels++; }
static ngx_btt_torrent_t * ngx_btt_get_torrent(ngx_btt_conf_t *bcf, ngx_btt_ctx_t *ctx, ngx_rbtree_t *tree, ngx_queue_t *queue) { ngx_queue_t *q; ngx_btt_torrent_t *t; t = NULL; if (!ngx_queue_empty(&bcf->btt->free_torrents)) { q = ngx_queue_head(&bcf->btt->free_torrents); ngx_queue_remove(q); t = ngx_queue_data(q, ngx_btt_torrent_t, queue); } if (t == NULL) { t = ngx_slab_alloc_locked(bcf->pool, sizeof(ngx_btt_torrent_t)); if (t == NULL) { return NULL; } } /* TODO */ ngx_rbtree_init(&t->peers_rbtree, &t->peers_sentinel, ngx_btt_insert_peer); ngx_queue_init(&t->peers_queue); ngx_memcpy(t->info_hash, ctx->info_hash, sizeof(ctx->info_hash)); t->node.key = ngx_crc32_short(ctx->info_hash, sizeof(ctx->info_hash)); ngx_rbtree_insert(tree, &t->node); ngx_queue_insert_head(queue, &t->queue); return t; }
int ngx_tcp_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, size_t key_len, int value_type, u_char *str_value_buf, size_t str_value_len, double num_value, int exptime, int user_flags, char **errmsg, int *forcible) { int i, n; u_char c, *p; uint32_t hash; ngx_int_t rc; ngx_time_t *tp; ngx_rbtree_node_t *node; ngx_tcp_lua_shdict_ctx_t *ctx; ngx_tcp_lua_shdict_node_t *sd; if (zone == NULL) { return NGX_ERROR; } dd("exptime: %d", exptime); ctx = zone->data; *forcible = 0; hash = ngx_crc32_short(key, key_len); switch (value_type) { case LUA_TSTRING: /* do nothing */ break; case LUA_TNUMBER: dd("num value: %lf", num_value); str_value_buf = (u_char *) &num_value; str_value_len = sizeof(double); break; case LUA_TBOOLEAN: c = num_value ? 1 : 0; str_value_buf = &c; str_value_len = sizeof(u_char); break; case LUA_TNIL: if (op & (NGX_TCP_LUA_SHDICT_ADD|NGX_TCP_LUA_SHDICT_REPLACE)) { *errmsg = "attempt to add or replace nil values"; return NGX_ERROR; } str_value_buf = NULL; str_value_len = 0; break; default: *errmsg = "unsupported value type"; return NGX_ERROR; } ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_tcp_lua_shdict_expire(ctx, 1); #endif rc = ngx_tcp_lua_shdict_lookup(zone, hash, key, key_len, &sd); dd("lookup returns %d", (int) rc); if (op & NGX_TCP_LUA_SHDICT_REPLACE) { if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "not found"; return NGX_DECLINED; } /* rc == NGX_OK */ goto replace; } if (op & NGX_TCP_LUA_SHDICT_ADD) { if (rc == NGX_OK) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "exists"; return NGX_DECLINED; } if (rc == NGX_DONE) { /* exists but expired */ dd("go to replace"); goto replace; } /* rc == NGX_DECLINED */ dd("go to insert"); goto insert; } if (rc == NGX_OK || rc == NGX_DONE) { if (value_type == LUA_TNIL) { goto remove; } replace: if (str_value_buf && str_value_len == (size_t) sd->value_len) { ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: found old entry and value " "size matched, reusing it"); ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); sd->key_len = (u_short) key_len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) exptime; } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) str_value_len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key, key_len); ngx_memcpy(p, str_value_buf, str_value_len); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: found old entry bug value size " "NOT matched, removing it first"); remove: ngx_queue_remove(&sd->queue); node = (ngx_rbtree_node_t *) ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); } insert: /* rc == NGX_DECLINED or value size unmatch */ if (str_value_buf == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: creating a new entry"); n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_tcp_lua_shdict_node_t, data) + key_len + str_value_len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { if (op & NGX_TCP_LUA_SHDICT_SAFE_STORE) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "no memory"; return NGX_ERROR; } ngx_log_debug2(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: overriding non-expired items " "due to memory shortage for entry \"%*s\"", key_len, key); for (i = 0; i < 30; i++) { if (ngx_tcp_lua_shdict_expire(ctx, 0) == 0) { break; } *forcible = 1; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node != NULL) { goto allocated; } } ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "no memory"; return NGX_ERROR; } allocated: sd = (ngx_tcp_lua_shdict_node_t *) &node->color; node->key = hash; sd->key_len = (u_short) key_len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) exptime; } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) str_value_len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key, key_len); ngx_memcpy(p, str_value_buf, str_value_len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; }
/* * 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_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account) { size_t size; ngx_int_t rc, excess; ngx_msec_t now; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lr; now = ngx_current_msec; ctx = limit->shm_zone->data; node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ lr = (ngx_http_limit_req_node_t *) &node->color; rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len); if (rc == 0) { ngx_queue_remove(&lr->queue); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); ms = (ngx_msec_int_t) (now - lr->last); excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; if (excess < 0) { excess = 0; } *ep = excess; if ((ngx_uint_t) excess > limit->burst) { return NGX_BUSY; } if (account) { lr->excess = excess; lr->last = now; return NGX_OK; } lr->count++; ctx->node = lr; return NGX_AGAIN; } node = (rc < 0) ? node->left : node->right; } *ep = 0; size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_req_node_t, data) + key->len; ngx_http_limit_req_expire(ctx, 1); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_http_limit_req_expire(ctx, 0); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "could not allocate node%s", ctx->shpool->log_ctx); return NGX_ERROR; } } node->key = hash; lr = (ngx_http_limit_req_node_t *) &node->color; lr->len = (u_short) key->len; lr->excess = 0; ngx_memcpy(lr->data, key->data, key->len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); if (account) { lr->last = now; lr->count = 0; return NGX_OK; } lr->last = 0; lr->count = 1; ctx->node = lr; return NGX_AGAIN; }
static ngx_int_t ngx_http_limit_req2_handler(ngx_http_request_t *r) { size_t n, total_len; uint32_t hash; ngx_int_t rc; ngx_msec_t delay_time; ngx_uint_t excess, delay_excess, delay_postion, nodelay, i; ngx_time_t *tp; ngx_rbtree_node_t *node; ngx_http_limit_req2_t *limit_req2; ngx_http_limit_req2_ctx_t *ctx; ngx_http_limit_req2_node_t *lr; ngx_http_limit_req2_conf_t *lrcf; delay_excess = 0; excess = 0; delay_postion = 0; nodelay = 0; ctx = NULL; rc = NGX_DECLINED; if (r->main->limit_req_set) { return NGX_DECLINED; } lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req2_module); if (lrcf->rules == NULL) { return NGX_DECLINED; } if (!lrcf->enable) { return NGX_DECLINED; } /* filter whitelist */ if (ngx_http_limit_req2_ip_filter(r, lrcf) == NGX_OK) { return NGX_DECLINED; } /* to match limit_req2 rule*/ limit_req2 = lrcf->rules->elts; for (i = 0; i < lrcf->rules->nelts; i++) { ctx = limit_req2[i].shm_zone->data; ngx_crc32_init(hash); total_len = 0; total_len = ngx_http_limit_req2_copy_variables(r, &hash, ctx, NULL); if (total_len == 0) { continue; } ngx_crc32_final(hash); ngx_shmtx_lock(&ctx->shpool->mutex); ngx_http_limit_req2_expire(r, ctx, 1); rc = ngx_http_limit_req2_lookup(r, &limit_req2[i], hash, &excess); ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit_req2 module: %i %ui.%03ui " "hash is %ui total_len is %i", rc, excess / 1000, excess % 1000, hash, total_len); /* first limit_req2 */ if (rc == NGX_DECLINED) { n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_req2_node_t, data) + total_len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_http_limit_req2_expire(r, ctx, 0); node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_HTTP_SERVICE_UNAVAILABLE; } } lr = (ngx_http_limit_req2_node_t *) &node->color; node->key = hash; lr->len = (u_char) total_len; tp = ngx_timeofday(); lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); lr->excess = 0; ngx_http_limit_req2_copy_variables(r, &hash, ctx, lr); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_shmtx_unlock(&ctx->shpool->mutex); continue; } ngx_shmtx_unlock(&ctx->shpool->mutex); if (rc == NGX_BUSY || rc == NGX_ERROR) { break; } /* NGX_AGAIN or NGX_OK */ if (delay_excess < excess) { delay_excess = excess; nodelay = limit_req2[i].nodelay; delay_postion = i; } } r->main->limit_req_set = 1; if (rc == NGX_BUSY || rc == NGX_ERROR) { if (rc == NGX_BUSY) { ngx_log_error(lrcf->limit_log_level, r->connection->log, 0, "limiting requests, excess: %ui.%03ui by zone \"%V\"", excess / 1000, excess % 1000, &limit_req2[i].shm_zone->shm.name); } if (rc == NGX_ERROR || limit_req2[i].forbid_action.len == 0) { return NGX_HTTP_SERVICE_UNAVAILABLE; } else if (limit_req2[i].forbid_action.data[0] == '@') { ngx_log_error(lrcf->limit_log_level, r->connection->log, 0, "limiting requests, forbid_action is %V", &limit_req2[i].forbid_action); (void) ngx_http_named_location(r, &limit_req2[i].forbid_action); } else { ngx_log_error(lrcf->limit_log_level, r->connection->log, 0, "limiting requests, forbid_action is %V", &limit_req2[i].forbid_action); (void) ngx_http_internal_redirect(r, &limit_req2[i].forbid_action, &r->args); } ngx_http_finalize_request(r, NGX_DONE); return NGX_DONE; } /* rc = NGX_AGAIN */ if (delay_excess != 0) { if (nodelay) { return NGX_DECLINED; } delay_time = (ngx_msec_t) delay_excess * 1000 / ctx->rate; ngx_log_error(lrcf->delay_log_level, r->connection->log, 0, "delaying request," "excess: %ui.%03ui, by zone \"%V\", delay \"%ui\" s", delay_excess / 1000, delay_excess % 1000, &limit_req2[delay_postion].shm_zone->shm.name, delay_time); if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->read_event_handler = ngx_http_test_reading; r->write_event_handler = ngx_http_limit_req2_delay; ngx_add_timer(r->connection->write, delay_time); return NGX_AGAIN; } /* rc == NGX_OK or rc == NGX_DECLINED */ return NGX_DECLINED; }
static ngx_int_t ngx_http_ip_behavior_lookup(ngx_http_request_t *r, ngx_uint_t hash, ngx_http_ip_behavior_ctx_t *ctx, ngx_http_ip_behavior_conf_t *ilcf) { size_t size; ngx_int_t rc; ngx_time_t *tp; ngx_msec_t now; ngx_rbtree_node_t *node, *sentinel; ngx_http_ip_behavior_node_t *ibn; tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; rc = -1; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ ibn = (ngx_http_ip_behavior_node_t *) &node->color; rc = ngx_memn2cmp(r->connection->addr_text.data, ibn->addr, r->connection->addr_text.len, (size_t) ibn->len); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip behavior lookup is : %i", rc); if (rc == 0) { /* found the node */ /* check expire */ if (ngx_abs((ngx_msec_int_t)(now - ibn->last)) >= ctx->sample_cycle) { /* node is expired, clear counters */ ibn->insensitive = 0; ibn->total = 0; ibn->bad_response = 0; } ngx_queue_remove(&ibn->queue); ngx_queue_insert_head(&ctx->sh->queue, &ibn->queue); ibn->last = now; if (ilcf->type & TYPE_SENSITIVE_URL) { if (!ilcf->sensitive) { ibn->insensitive++; } } else if (ilcf->type & TYPE_BAD_RESPONSE) { /* TODO: support bad response behavior */ } else { /* should never be here */ } /* total can be zero when it grows to big, * so need to reset all the counters */ if (++ibn->total == 0) { ibn->insensitive = 0; ibn->bad_response = 0; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip behavior node reset: %V", &r->connection->addr_text); } if (ibn->total >= (ngx_uint_t)ctx->sample_base) { r->insensitive_percent = (float)ibn->insensitive / (float)ibn->total * 100; r->bad_response_percent = (float)ibn->bad_response / (float)ibn->total * 100; } ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip behavior find node: %V, total: %i, insens: %i," " percent: %i", &r->connection->addr_text, ibn->total, ibn->insensitive, r->insensitive_percent); return NGX_OK; } node = (rc < 0) ? node->left : node->right; } /* create a new node */ size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_ip_behavior_node_t, addr) + r->connection->addr_text.len; node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_http_ip_behavior_expire(ctx, 1); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { return NGX_ERROR; } } node->key = hash; ibn = (ngx_http_ip_behavior_node_t *) &node->color; ibn->len = (u_char) r->connection->addr_text.len; ngx_memcpy(ibn->addr, r->connection->addr_text.data, r->connection->addr_text.len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &ibn->queue); ibn->last = now; ibn->total = 1; ibn->insensitive = !ilcf->sensitive; ibn->bad_response = 0; ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip behavior new node: %V, total: %i, insens: %i," " percent: %i", &r->connection->addr_text, ibn->total, ibn->insensitive, r->insensitive_percent); return NGX_OK; }
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; }
/* NGX_OK NGX_AGAIN NGX_ERROR NGX_DECLINED: cache索引表中没有查找到,创建node并插入到红黑树和队列中,并且设置node节点到 c->node */ static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) { ngx_int_t rc; ngx_http_file_cache_node_t *fcn; // 对共享内存区上锁 ngx_shmtx_lock(&cache->shpool->mutex); // 在函数 ngx_init_zone_pool()中设置并创建 fcn = c->node; if (fcn == NULL) { // 在cache中查找key fcn = ngx_http_file_cache_lookup(cache, c->key); } // 在共享内存中找到cache if (fcn) { // 在队列中删除???? ngx_queue_remove(&fcn->queue); // ???? if (c->node == NULL) { fcn->uses++; fcn->count++; } // ???? if (fcn->error) { if (fcn->valid_sec < ngx_time()) { goto renew; } rc = NGX_OK; goto done; } if (fcn->exists || fcn->uses >= c->min_uses) { c->exists = fcn->exists; if (fcn->body_start) { c->body_start = fcn->body_start; } rc = NGX_OK; goto done; } rc = NGX_AGAIN; goto done; } //################ 在共享内存cache索引表中没有查找到此cache ################ // 1. 申请一个cache节点 fcn = ngx_slab_alloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { // 共享内存空间不足申请一个cache节点时,强制过期一个cache节点 ngx_shmtx_unlock(&cache->shpool->mutex); (void) ngx_http_file_cache_forced_expire(cache); ngx_shmtx_lock(&cache->shpool->mutex); fcn = ngx_slab_alloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { rc = NGX_ERROR; goto failed; } } // 2. 拷贝节点key ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t)); ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)], NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); // 3. 插入到红黑树中 ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node); fcn->uses = 1; fcn->count = 1; fcn->updating = 0; fcn->deleting = 0; renew: rc = NGX_DECLINED; fcn->valid_msec = 0; fcn->error = 0; fcn->exists = 0; fcn->valid_sec = 0; fcn->uniq = 0; fcn->body_start = 0; fcn->fs_size = 0; done: fcn->expire = ngx_time() + cache->inactive; ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); c->uniq = fcn->uniq; c->error = fcn->error; c->node = fcn; // 设置node节点 failed: ngx_shmtx_unlock(&cache->shpool->mutex); return rc; }
ngx_int_t ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, ngx_open_file_info_t *of, ngx_pool_t *pool) { time_t now; uint32_t hash; ngx_int_t rc; ngx_pool_cleanup_t *cln; ngx_cached_open_file_t *file; ngx_pool_cleanup_file_t *clnf; ngx_open_file_cache_cleanup_t *ofcln; of->err = 0; if (cache == NULL) { cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); if (cln == NULL) { return NGX_ERROR; } rc = ngx_open_and_stat_file(name->data, of, pool->log); if (rc == NGX_OK && !of->is_dir) { cln->handler = ngx_pool_cleanup_file; clnf = cln->data; clnf->fd = of->fd; clnf->name = name->data; clnf->log = pool->log; } return rc; } cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t)); if (cln == NULL) { return NGX_ERROR; } now = ngx_time(); hash = ngx_crc32_long(name->data, name->len); file = ngx_open_file_lookup(cache, name, hash); if (file) { file->uses++; ngx_queue_remove(&file->queue); if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) { /* file was not used often enough to keep open */ rc = ngx_open_and_stat_file(name->data, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; } goto add_event; } if ((file->event && file->use_event) || (file->event == NULL && now - file->created < of->valid)) { if (file->err == 0) { of->fd = file->fd; of->uniq = file->uniq; of->mtime = file->mtime; of->size = file->size; of->is_dir = file->is_dir; of->is_file = file->is_file; of->is_link = file->is_link; of->is_exec = file->is_exec; if (!file->is_dir) { file->count++; ngx_open_file_add_event(cache, file, of, pool->log); } } else { of->err = file->err; } goto found; } ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0, "retest open file: %s, fd:%d, c:%d, e:%d", file->name, file->fd, file->count, file->err); if (file->is_dir) { /* * chances that directory became file are very small * so test_dir flag allows to use a single syscall * in ngx_file_info() instead of three syscalls */ of->test_dir = 1; } rc = ngx_open_and_stat_file(name->data, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; } if (of->is_dir) { if (file->is_dir || file->err) { goto update; } /* file became directory */ } else if (of->err == 0) { /* file */ if (file->is_dir || file->err) { goto add_event; } if (of->uniq == file->uniq && of->mtime == file->mtime && of->size == file->size) { if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%s\" failed", name->data); } of->fd = file->fd; file->count++; if (file->event) { file->use_event = 1; goto renew; } ngx_open_file_add_event(cache, file, of, pool->log); goto renew; } /* file was changed */ } else { /* error to cache */ if (file->err || file->is_dir) { goto update; } /* file was removed, etc. */ } if (file->count == 0) { ngx_open_file_del_event(file); if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%s\" failed", name->data); } goto add_event; } ngx_rbtree_delete(&cache->rbtree, &file->node); cache->current--; file->close = 1; goto create; } /* not found */ rc = ngx_open_and_stat_file(name->data, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; } create: if (cache->current >= cache->max) { ngx_expire_old_cached_files(cache, 0, pool->log); } file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log); if (file == NULL) { goto failed; } file->name = ngx_alloc(name->len + 1, pool->log); if (file->name == NULL) { ngx_free(file); file = NULL; goto failed; } ngx_cpystrn(file->name, name->data, name->len + 1); file->node.key = hash; ngx_rbtree_insert(&cache->rbtree, &file->node); cache->current++; file->uses = 1; file->count = 0; file->use_event = 0; file->event = NULL; add_event: ngx_open_file_add_event(cache, file, of, pool->log); update: file->fd = of->fd; file->err = of->err; if (of->err == 0) { file->uniq = of->uniq; file->mtime = of->mtime; file->size = of->size; file->close = 0; file->is_dir = of->is_dir; file->is_file = of->is_file; file->is_link = of->is_link; file->is_exec = of->is_exec; if (!of->is_dir) { file->count++; } } renew: file->created = now; found: file->accessed = now; ngx_queue_insert_head(&cache->expire_queue, &file->queue); ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0, "cached open file: %s, fd:%d, c:%d, e:%d, u:%d", file->name, file->fd, file->count, file->err, file->uses); if (of->err == 0) { if (!of->is_dir) { cln->handler = ngx_open_file_cleanup; ofcln = cln->data; ofcln->cache = cache; ofcln->file = file; ofcln->min_uses = of->min_uses; ofcln->log = pool->log; } return NGX_OK; } return NGX_ERROR; failed: if (file) { ngx_rbtree_delete(&cache->rbtree, &file->node); cache->current--; if (file->count == 0) { if (file->fd != NGX_INVALID_FILE) { if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file->name); } } ngx_free(file->name); ngx_free(file); } else { file->close = 1; } } if (of->fd != NGX_INVALID_FILE) { if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%s\" failed", name->data); } } return NGX_ERROR; }
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; }
static void ngx_http_cloudrouter_peer_preconnect_read(ngx_event_t *rev) { ngx_connection_t *c; ngx_http_cloudrouter_peer_preconnect_data_t *pcd; ngx_http_upstream_t *u; ngx_http_request_t *r; int i; hs_route_t *route; c = rev->data; pcd = c->data; r = pcd->r; u = pcd->u; route = &pcd->route; ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "preconnect: read"); if (pcd->done>0) { return; } if (r->main==NULL||r->request_complete||r->pool==NULL||r!=r->main) { ngx_close_connection(c); c->destroyed = 1; return; } if (rev->timedout) { ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, "cloudrouter preconnect server timed out"); return ngx_http_cloudrouter_peer_preconnect_close(c, pcd, NGX_ERROR); } if (pcd->buf==NULL) { int size = sizeof(char)*1000; ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "creating buf"); pcd->buf = ngx_pcalloc(r->pool, size); pcd->bufend = pcd->buf+size; pcd->bufpos = pcd->buf; if (pcd->buf==NULL) { ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ENOMEM, "preconnect: read: could not allocate buf"); return ngx_http_cloudrouter_peer_preconnect_close(c, pcd, NGX_ERROR); } } /* * Protocol format: * IP1\nPORT1\n * (optional) IPz\nPORTz\n * -- */ int bufsize = pcd->bufend - pcd->bufpos; ngx_int_t received; if (bufsize > 0) { received = ngx_recv(c, pcd->bufpos, bufsize); } else { received = 0; } if (received==NGX_AGAIN) { return; } else if (received>=0) { pcd->bufpos += received; for (i=0;i<(pcd->bufpos-pcd->buf);i++) { if (*(pcd->buf + i )=='-') { pcd->end_marker_count++; } } if (pcd->end_marker_count>=2) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "CloudRouter preconnect: message complete"); ngx_http_cloudrouter_peer_t *peer = (ngx_http_cloudrouter_peer_t *)u->peer.data; unsigned char* next = pcd->buf; int new_node = 0; ngx_shmtx_lock(&ngx_http_cloudrouter_shpool->mutex); ngx_http_cloudrouter_node_t *e = ngx_http_cloudrouter_get_locked(route); if (e == NULL) { /* likely() */ e = ngx_slab_alloc_locked(ngx_http_cloudrouter_shpool, sizeof *e); new_node = 1; e->node.key = ngx_http_cloudrouter_hash_route(route); (void)ngx_copy(e->di_name, route->di_name, sizeof(route->di_name)); e->di_nlen = route->di_nlen; } else { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "CloudRouter preconnect: reusing existing node"); } e->timestamp = time(NULL); ngx_http_cloudrouter_clear_remotes_locked(e, rev->log); while (next < pcd->bufpos) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "CloudRouter preconnect: parsing message"); unsigned char *ip = NULL; unsigned char *port = NULL; ip = next; while (++next < pcd->bufpos) { if (*(next-1) == '\n') { if (ip && port) break; port = next; } } if (ip && port) { ngx_http_cloudrouter_remote_t *remote = ngx_http_cloudrouter_add_remote_locked(e); int iplen = port-ip-1; iplen = iplen > 16 ? 16 : iplen; remote->inet_addr = ngx_inet_addr(ip, iplen); if (remote->inet_addr == INADDR_NONE) { ngx_log_error(NGX_LOG_ERR, rev->log, NGX_EINVAL, "CloudRouter preconnect: IP address from Agent invalid for route %s", e->di_name); goto failure; } int portlen = next-port-1; remote->port_n = htons(ngx_atoi(port,portlen)); ngx_log_debug7(NGX_LOG_DEBUG_HTTP, rev->log, 0, "CloudRouter preconnect: cached values SET: e=%p rem=%p ts=%d %s[%d] %uxD:%d", e, remote, e->timestamp, e->di_name, e->di_nlen, remote->inet_addr, remote->port_n); } } if (!e->remote) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, rev->log, 0, "CloudRouter preconnect: Agent sent no remotes"); goto failure; } ngx_http_cloudrouter_set_hostandport(r, peer, e); if (new_node) { ngx_rbtree_insert(ngx_http_cloudrouter_rbtree, &e->node); } ngx_shmtx_unlock(&ngx_http_cloudrouter_shpool->mutex); return ngx_http_cloudrouter_peer_preconnect_close(c, pcd, NGX_OK); failure: if (!new_node) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pcd->r->connection->log, 0, "peer_preconnect_read: calling rbtree_delete"); ngx_rbtree_delete(ngx_http_cloudrouter_rbtree, &e->node); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pcd->r->connection->log, 0, "peer_preconnect_read: calling free_node_locked"); ngx_http_cloudrouter_free_node_locked(e, rev->log); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pcd->r->connection->log, 0, "peer_preconnect_read: calling shmtx_unlock"); ngx_shmtx_unlock(&ngx_http_cloudrouter_shpool->mutex); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pcd->r->connection->log, 0, "peer_preconnect_read: calling peer_preconnect_close"); return ngx_http_cloudrouter_peer_preconnect_close(c, pcd, NGX_ERROR); } return; } /* unknown error condition from ngx_recv */ return; }
static ngx_int_t ngx_http_tfs_create_info_node(ngx_http_tfs_t *t, ngx_http_tfs_rc_ctx_t *rc_ctx, u_char *data, ngx_str_t appkey) { u_char *p; size_t n; uint32_t len; ngx_int_t rc; ngx_rbtree_node_t *node; ngx_http_tfs_rcs_info_t *rc_info_node; rc_info_node = NULL; n = offsetof(ngx_rbtree_node_t, color) + sizeof(ngx_http_tfs_rcs_info_t); node = ngx_slab_alloc_locked(rc_ctx->shpool, n); if (node == NULL) { ngx_http_tfs_expire_and_alloc(node, n); } rc_info_node = (ngx_http_tfs_rcs_info_t *) &node->color; node->key = ngx_murmur_hash2(appkey.data, appkey.len); rc_info_node->appkey.data = ngx_slab_alloc_locked(rc_ctx->shpool, appkey.len); if (rc_info_node->appkey.data == NULL) { ngx_http_tfs_rc_server_expire(rc_ctx); rc_info_node->appkey.data = ngx_slab_alloc_locked(rc_ctx->shpool, appkey.len); if (rc_info_node->appkey.data == NULL) { goto login_error; } } ngx_memcpy(rc_info_node->appkey.data, appkey.data, appkey.len); rc_info_node->appkey.len = appkey.len; /* parse session id */ len = *((uint32_t *) data); p = data + sizeof(uint32_t); if (len <= 0) { rc_info_node->session_id.len = 0; goto login_error; } rc_info_node->session_id.len = len - 1; rc_info_node->session_id.data = ngx_slab_alloc_locked(rc_ctx->shpool, len); if (rc_info_node->session_id.data == NULL) { ngx_http_tfs_rc_server_expire(rc_ctx); rc_info_node->session_id.data = ngx_slab_alloc_locked(rc_ctx->shpool, rc_info_node->session_id.len); if (rc_info_node->session_id.data == NULL) { goto login_error; } } ngx_memcpy(rc_info_node->session_id.data, p, rc_info_node->session_id.len); p += rc_info_node->session_id.len + 1; /* parse rc info */ rc = ngx_http_tfs_parse_rc_info(rc_info_node, rc_ctx, p); if (rc == NGX_ERROR) { goto login_error; } t->rc_info_node = rc_info_node; ngx_rbtree_insert(&rc_ctx->sh->rbtree, node); ngx_queue_insert_head(&rc_ctx->sh->queue, &rc_info_node->queue); ngx_queue_insert_tail(&rc_ctx->sh->kp_queue, &rc_info_node->kp_queue); return NGX_OK; login_error: ngx_http_tfs_rc_server_destroy_node(rc_ctx, rc_info_node); t->rc_info_node = NULL; return NGX_ERROR; }
static ngx_http_reqstat_rbnode_t * ngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val) { size_t size; uint32_t hash; ngx_int_t rc; ngx_rbtree_node_t *node, *sentinel; ngx_http_reqstat_ctx_t *ctx; ngx_http_reqstat_rbnode_t *rs; ctx = shm_zone->data; hash = ngx_crc32_short(val->data, val->len); node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; ngx_shmtx_lock(&ctx->shpool->mutex); while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ rs = (ngx_http_reqstat_rbnode_t *) &node->color; rc = ngx_memn2cmp(val->data, rs->data, val->len, (size_t) rs->len); if (rc == 0) { ngx_shmtx_unlock(&ctx->shpool->mutex); return rs; } node = (rc < 0) ? node->left : node->right; } size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_reqstat_rbnode_t, data) + val->len; node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NULL; } node->key = hash; rs = (ngx_http_reqstat_rbnode_t *) &node->color; rs->len = val->len; ngx_memcpy(rs->data, val->data, val->len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &rs->queue); ngx_shmtx_unlock(&ctx->shpool->mutex); return rs; }
ngx_http_reqstat_rbnode_t * ngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val) { size_t size, len; uint32_t hash; ngx_int_t rc, excess; ngx_time_t *tp; ngx_msec_t now; ngx_queue_t *q; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_reqstat_ctx_t *ctx; ngx_http_reqstat_rbnode_t *rs; ctx = shm_zone->data; hash = ngx_murmur_hash2(val->data, val->len); node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); ngx_shmtx_lock(&ctx->shpool->mutex); while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ rs = (ngx_http_reqstat_rbnode_t *) &node->color; /* len < node->len */ if (val->len < (size_t) rs->len) { node = node->left; continue; } rc = ngx_strncmp(val->data, rs->data, (size_t) rs->len); if (rc == 0) { ms = (ngx_msec_int_t) (now - rs->last_visit); rs->excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000 + 1000; rs->last_visit = now; if (rs->excess > 0) { ngx_queue_remove(&rs->visit); ngx_queue_insert_head(&ctx->sh->visit, &rs->visit); } ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup exist: %*s", rs->len, rs->data); ngx_shmtx_unlock(&ctx->shpool->mutex); return rs; } node = (rc < 0) ? node->left : node->right; } rc = 0; node = NULL; size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_reqstat_rbnode_t, data) + ctx->key_len; if (ctx->alloc_already_fail == 0) { node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ctx->alloc_already_fail = 1; } } if (node == NULL) { /* try to free a vacant node */ q = ngx_queue_last(&ctx->sh->visit); rs = ngx_queue_data(q, ngx_http_reqstat_rbnode_t, visit); ms = (ngx_msec_int_t) (now - rs->last_visit); excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000; ngx_log_debug3(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup try recycle: %*s, %d", rs->len, rs->data, excess); if (excess < 0) { rc = 1; node = (ngx_rbtree_node_t *) ((char *) rs - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_queue_remove(&rs->visit); ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup recycle: %*s", rs->len, rs->data); rs->conn_total = 0; ngx_memzero((void *) &rs->bytes_in, size - offsetof(ngx_rbtree_node_t, color) - offsetof(ngx_http_reqstat_rbnode_t, bytes_in)); } else { ngx_shmtx_unlock(&ctx->shpool->mutex); return NULL; } } node->key = hash; rs = (ngx_http_reqstat_rbnode_t *) &node->color; len = ngx_min(ctx->key_len, (ssize_t) val->len); ngx_memcpy(rs->data, val->data, len); rs->len = len; ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->visit, &rs->visit); if (!rc) { ngx_queue_insert_head(&ctx->sh->queue, &rs->queue); } rs->last_visit = now; rs->excess = 1000; ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup build: %*s", rs->len, rs->data); ngx_shmtx_unlock(&ctx->shpool->mutex); return rs; }
// 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; }
ngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx) { u_char *name; ngx_resolver_t *r; ngx_resolver_node_t *rn; r = ctx->resolver; ctx->addr = ntohl(ctx->addr); /* lock addr mutex */ rn = ngx_resolver_lookup_addr(r, ctx->addr); 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->addr_expire_queue, &rn->queue); name = ngx_resolver_dup(r, rn->name, rn->nlen); if (name == NULL) { goto failed; } ctx->name.len = rn->nlen; ctx->name.data = name; /* unlock addr mutex */ ctx->state = NGX_OK; ctx->handler(ctx); ngx_resolver_free(r, name); return NGX_OK; } if (rn->waiting) { ctx->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; /* unlock addr mutex */ return NGX_OK; } ngx_queue_remove(&rn->queue); ngx_resolver_free(r, rn->query); rn->query = NULL; } else { rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); if (rn == NULL) { goto failed; } rn->node.key = ctx->addr; rn->query = NULL; ngx_rbtree_insert(&r->addr_rbtree, &rn->node); } if (ngx_resolver_create_addr_query(rn, ctx) != NGX_OK) { goto failed; } if (ngx_resolver_send_query(r, rn) != NGX_OK) { goto failed; } 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->addr_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->addr_resend_queue, &rn->queue); rn->cnlen = 0; rn->naddrs = 0; rn->name = NULL; rn->nlen = 0; rn->valid = 0; rn->waiting = ctx; /* unlock addr mutex */ ctx->state = NGX_AGAIN; return NGX_OK; failed: if (rn) { ngx_rbtree_delete(&r->addr_rbtree, &rn->node); if (rn->query) { ngx_resolver_free(r, rn->query); } ngx_resolver_free(r, rn); } /* unlock addr mutex */ if (ctx->event) { ngx_resolver_free(r, ctx->event); } ngx_resolver_free(r, ctx); return NGX_ERROR; }
static ngx_int_t ngx_http_limit_zone_handler(ngx_http_request_t *r) { size_t len, n; uint32_t hash; ngx_int_t rc; ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node, *sentinel; ngx_pool_cleanup_t *cln; ngx_http_variable_value_t *vv; ngx_http_limit_zone_ctx_t *ctx; ngx_http_limit_zone_node_t *lz; ngx_http_limit_zone_conf_t *lzcf; ngx_http_limit_zone_cleanup_t *lzcln; if (r->main->limit_zone_set) { return NGX_DECLINED; } lzcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_zone_module); if (lzcf->shm_zone == NULL) { return NGX_DECLINED; } ctx = lzcf->shm_zone->data; vv = ngx_http_get_indexed_variable(r, ctx->index); if (vv == NULL || vv->not_found) { return NGX_DECLINED; } len = vv->len; if (len == 0) { return NGX_DECLINED; } if (len > 255) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the value of the \"%V\" variable " "is more than 255 bytes: \"%v\"", &ctx->var, vv); return NGX_DECLINED; } r->main->limit_zone_set = 1; hash = ngx_crc32_short(vv->data, len); cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_zone_cleanup_t)); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } shpool = (ngx_slab_pool_t *) lzcf->shm_zone->shm.addr; ngx_shmtx_lock(&shpool->mutex); node = ctx->rbtree->root; sentinel = ctx->rbtree->sentinel; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ do { lz = (ngx_http_limit_zone_node_t *) &node->color; rc = ngx_memn2cmp(vv->data, lz->data, len, (size_t) lz->len); if (rc == 0) { if ((ngx_uint_t) lz->conn < lzcf->conn) { lz->conn++; goto done; } ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "limiting connections by zone \"%V\"", &lzcf->shm_zone->shm.name); return NGX_HTTP_SERVICE_UNAVAILABLE; } node = (rc < 0) ? node->left : node->right; } while (node != sentinel && hash == node->key); break; } n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_zone_node_t, data) + len; node = ngx_slab_alloc_locked(shpool, n); if (node == NULL) { ngx_shmtx_unlock(&shpool->mutex); return NGX_HTTP_SERVICE_UNAVAILABLE; } lz = (ngx_http_limit_zone_node_t *) &node->color; node->key = hash; lz->len = (u_char) len; lz->conn = 1; ngx_memcpy(lz->data, vv->data, len); ngx_rbtree_insert(ctx->rbtree, node); done: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit zone: %08XD %d", node->key, lz->conn); ngx_shmtx_unlock(&shpool->mutex); cln->handler = ngx_http_limit_zone_cleanup; lzcln = cln->data; lzcln->shm_zone = lzcf->shm_zone; lzcln->node = node; return NGX_DECLINED; }
static 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; }
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_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; }
ngx_int_t ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, ngx_open_file_info_t *of, ngx_pool_t *pool) { time_t now; uint32_t hash; ngx_int_t rc; ngx_file_info_t fi; ngx_pool_cleanup_t *cln; ngx_cached_open_file_t *file; ngx_pool_cleanup_file_t *clnf; ngx_open_file_cache_cleanup_t *ofcln; of->fd = NGX_INVALID_FILE; of->err = 0; if (cache == NULL) { if (of->test_only) { if (ngx_file_info_wrapper(name, of, &fi, pool->log) == NGX_FILE_ERROR) { return NGX_ERROR; } of->uniq = ngx_file_uniq(&fi); of->mtime = ngx_file_mtime(&fi); of->size = ngx_file_size(&fi); of->fs_size = ngx_file_fs_size(&fi); of->is_dir = ngx_is_dir(&fi); of->is_file = ngx_is_file(&fi); of->is_link = ngx_is_link(&fi); of->is_exec = ngx_is_exec(&fi); return NGX_OK; } cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); if (cln == NULL) { return NGX_ERROR; } rc = ngx_open_and_stat_file(name, of, pool->log); if (rc == NGX_OK && !of->is_dir) { cln->handler = ngx_pool_cleanup_file; clnf = cln->data; clnf->fd = of->fd; clnf->name = name->data; clnf->log = pool->log; } return rc; } cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t)); if (cln == NULL) { return NGX_ERROR; } now = ngx_time(); // 计算hash hash = ngx_crc32_long(name->data, name->len); // 根据名字查找对应的file对象 file = ngx_open_file_lookup(cache, name, hash); if (file) { file->uses++; ngx_queue_remove(&file->queue); if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) { /* file was not used often enough to keep open */ rc = ngx_open_and_stat_file(name, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; } goto add_event; } if (file->use_event || (file->event == NULL && (of->uniq == 0 || of->uniq == file->uniq) && now - file->created < of->valid #if (NGX_HAVE_OPENAT) && of->disable_symlinks == file->disable_symlinks && of->disable_symlinks_from == file->disable_symlinks_from #endif )) { if (file->err == 0) { of->fd = file->fd; of->uniq = file->uniq; of->mtime = file->mtime; of->size = file->size; of->is_dir = file->is_dir; of->is_file = file->is_file; of->is_link = file->is_link; of->is_exec = file->is_exec; of->is_directio = file->is_directio; if (!file->is_dir) { file->count++; ngx_open_file_add_event(cache, file, of, pool->log); } } else { of->err = file->err; #if (NGX_HAVE_OPENAT) of->failed = file->disable_symlinks ? ngx_openat_file_n : ngx_open_file_n; #else of->failed = ngx_open_file_n; #endif } goto found; } ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0, "retest open file: %s, fd:%d, c:%d, e:%d", file->name, file->fd, file->count, file->err); if (file->is_dir) { /* * chances that directory became file are very small * so test_dir flag allows to use a single syscall * in ngx_file_info() instead of three syscalls */ of->test_dir = 1; } of->fd = file->fd; of->uniq = file->uniq; // 打开文件,保存文件信息 rc = ngx_open_and_stat_file(name, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; } if (of->is_dir) { if (file->is_dir || file->err) { goto update; } /* file became directory */ } else if (of->err == 0) { /* file */ if (file->is_dir || file->err) { goto add_event; } if (of->uniq == file->uniq) { if (file->event) { file->use_event = 1; } of->is_directio = file->is_directio; goto update; } /* file was changed */ } else { /* error to cache */ if (file->err || file->is_dir) { goto update; } /* file was removed, etc. */ } if (file->count == 0) { ngx_open_file_del_event(file); if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%V\" failed", name); } goto add_event; } ngx_rbtree_delete(&cache->rbtree, &file->node); cache->current--; file->close = 1; goto create; } /* not found */ rc = ngx_open_and_stat_file(name, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; } create: // max为open_file_cache命令中定义的那个max指令, // 而current也就是当前cache的文件个数 if (cache->current >= cache->max) { // 如果大于max,则需要强制expire几个元素 ngx_expire_old_cached_files(cache, 0, pool->log); } file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log); if (file == NULL) { goto failed; } file->name = ngx_alloc(name->len + 1, pool->log); if (file->name == NULL) { ngx_free(file); file = NULL; goto failed; } ngx_cpystrn(file->name, name->data, name->len + 1); file->node.key = hash; ngx_rbtree_insert(&cache->rbtree, &file->node); cache->current++; file->uses = 1; file->count = 0; file->use_event = 0; file->event = NULL; add_event: ngx_open_file_add_event(cache, file, of, pool->log); update: file->fd = of->fd; file->err = of->err; #if (NGX_HAVE_OPENAT) file->disable_symlinks = of->disable_symlinks; file->disable_symlinks_from = of->disable_symlinks_from; #endif if (of->err == 0) { file->uniq = of->uniq; file->mtime = of->mtime; file->size = of->size; file->close = 0; file->is_dir = of->is_dir; file->is_file = of->is_file; file->is_link = of->is_link; file->is_exec = of->is_exec; file->is_directio = of->is_directio; if (!of->is_dir) { file->count++; } } file->created = now; found: // 更新存取时间 file->accessed = now; // 将文件插入到超时队列中 ngx_queue_insert_head(&cache->expire_queue, &file->queue); ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0, "cached open file: %s, fd:%d, c:%d, e:%d, u:%d", file->name, file->fd, file->count, file->err, file->uses); if (of->err == 0) { if (!of->is_dir) { // 这里很关键,将cln的handler cln->handler = ngx_open_file_cleanup; ofcln = cln->data; ofcln->cache = cache; ofcln->file = file; ofcln->min_uses = of->min_uses; ofcln->log = pool->log; } return NGX_OK; } return NGX_ERROR; failed: if (file) { ngx_rbtree_delete(&cache->rbtree, &file->node); cache->current--; if (file->count == 0) { if (file->fd != NGX_INVALID_FILE) { if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file->name); } } ngx_free(file->name); ngx_free(file); } else { file->close = 1; } } if (of->fd != NGX_INVALID_FILE) { if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%V\" failed", name); } } 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 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_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash, u_char *data, size_t len, ngx_uint_t *ep, ngx_uint_t account) { size_t size; ngx_int_t rc, excess; ngx_time_t *tp; ngx_msec_t now; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lr; tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); ctx = limit->shm_zone->data; node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ lr = (ngx_http_limit_req_node_t *) &node->color; rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len); if (rc == 0) { ngx_queue_remove(&lr->queue); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); ms = (ngx_msec_int_t) (now - lr->last); excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; if (excess < 0) { excess = 0; } *ep = excess; if ((ngx_uint_t) excess > limit->burst) { return NGX_BUSY; } if (account) { lr->excess = excess; lr->last = now; return NGX_OK; } lr->count++; ctx->node = lr; return NGX_AGAIN; } node = (rc < 0) ? node->left : node->right; } *ep = 0; size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_req_node_t, data) + len; ngx_http_limit_req_expire(ctx, 1); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_http_limit_req_expire(ctx, 0); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { return NGX_ERROR; } } node->key = hash; lr = (ngx_http_limit_req_node_t *) &node->color; lr->len = (u_char) len; lr->excess = 0; ngx_memcpy(lr->data, data, len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); if (account) { lr->last = now; lr->count = 0; return NGX_OK; } lr->last = 0; lr->count = 1; ctx->node = lr; return NGX_AGAIN; }
static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) { ngx_int_t rc; ngx_http_file_cache_node_t *fcn; ngx_shmtx_lock(&cache->shpool->mutex); fcn = ngx_http_file_cache_lookup(cache, c->key); if (fcn) { ngx_queue_remove(&fcn->queue); if (fcn->error) { if (fcn->valid_sec < ngx_time()) { goto renew; } rc = NGX_OK; goto done; } fcn->uses++; fcn->count++; if (fcn->exists) { c->exists = fcn->exists; c->body_start = fcn->body_start; rc = NGX_OK; goto done; } if (fcn->uses >= c->min_uses) { c->exists = fcn->exists; c->body_start = fcn->body_start; rc = NGX_OK; } else { rc = NGX_AGAIN; } goto done; } fcn = ngx_slab_alloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { ngx_shmtx_unlock(&cache->shpool->mutex); (void) ngx_http_file_cache_forced_expire(cache); ngx_shmtx_lock(&cache->shpool->mutex); fcn = ngx_slab_alloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { rc = NGX_ERROR; goto failed; } } ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t)); ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)], NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node); renew: rc = NGX_DECLINED; fcn->uses = 1; fcn->count = 1; fcn->valid_msec = 0; fcn->error = 0; fcn->exists = 0; fcn->valid_sec = 0; fcn->uniq = 0; fcn->body_start = 0; fcn->length = 0; done: fcn->expire = ngx_time() + cache->inactive; ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); c->uniq = fcn->uniq; c->error = fcn->error; c->node = fcn; failed: ngx_shmtx_unlock(&cache->shpool->mutex); return rc; }
static ngx_int_t ngx_http_limit_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; }
static int ngx_tcp_lua_shdict_set_helper(lua_State *L, int flags) { int i, n; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_tcp_lua_shdict_ctx_t *ctx; ngx_tcp_lua_shdict_node_t *sd; ngx_str_t value; int value_type; double num; u_char c; lua_Number exptime = 0; u_char *p; ngx_rbtree_node_t *node; ngx_time_t *tp; ngx_shm_zone_t *zone; int forcible = 0; /* indicates whether to foricibly override other * valid entries */ int32_t user_flags = 0; n = lua_gettop(L); if (n != 3 && n != 4 && n != 5) { return luaL_error(L, "expecting 3, 4 or 5 arguments, " "but only seen %d", n); } zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad \"zone\" argument"); } ctx = zone->data; if (lua_isnil(L, 2)) { lua_pushnil(L); lua_pushliteral(L, "nil key"); return 2; } key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { lua_pushnil(L); lua_pushliteral(L, "empty key"); return 2; } if (key.len > 65535) { lua_pushnil(L); lua_pushliteral(L, "key too long"); return 2; } hash = ngx_crc32_short(key.data, key.len); value_type = lua_type(L, 3); switch (value_type) { case LUA_TSTRING: value.data = (u_char *) lua_tolstring(L, 3, &value.len); break; case LUA_TNUMBER: value.len = sizeof(double); num = lua_tonumber(L, 3); value.data = (u_char *) # break; case LUA_TBOOLEAN: value.len = sizeof(u_char); c = lua_toboolean(L, 3) ? 1 : 0; value.data = &c; break; case LUA_TNIL: if (flags & (NGX_TCP_LUA_SHDICT_ADD|NGX_TCP_LUA_SHDICT_REPLACE)) { lua_pushnil(L); lua_pushliteral(L, "attempt to add or replace nil values"); return 2; } ngx_str_null(&value); break; default: lua_pushnil(L); lua_pushliteral(L, "bad value type"); return 2; } if (n >= 4) { exptime = luaL_checknumber(L, 4); if (exptime < 0) { exptime = 0; } } if (n == 5) { user_flags = (uint32_t) luaL_checkinteger(L, 5); } ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_tcp_lua_shdict_expire(ctx, 1); #endif rc = ngx_tcp_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returned %d", (int) rc); if (flags & NGX_TCP_LUA_SHDICT_REPLACE) { if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "not found"); lua_pushboolean(L, forcible); return 3; } /* rc == NGX_OK */ goto replace; } if (flags & NGX_TCP_LUA_SHDICT_ADD) { if (rc == NGX_OK) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "exists"); lua_pushboolean(L, forcible); return 3; } if (rc == NGX_DONE) { /* exists but expired */ dd("go to replace"); goto replace; } /* rc == NGX_DECLINED */ dd("go to insert"); goto insert; } if (rc == NGX_OK || rc == NGX_DONE) { if (value_type == LUA_TNIL) { goto remove; } replace: if (value.data && value.len == (size_t) sd->value_len) { ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: found old entry and value " "size matched, reusing it"); ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); sd->key_len = (u_short) key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) (exptime * 1000); } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) value.len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key.data, key.len); ngx_memcpy(p, value.data, value.len); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, forcible); return 3; } ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: found old entry bug value size " "NOT matched, removing it first"); remove: ngx_queue_remove(&sd->queue); node = (ngx_rbtree_node_t *) ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); } insert: /* rc == NGX_DECLINED or value size unmatch */ if (value.data == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, 0); return 3; } ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: creating a new entry"); n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_tcp_lua_shdict_node_t, data) + key.len + value.len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { if (flags & NGX_TCP_LUA_SHDICT_SAFE_STORE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "no memory"); return 2; } ngx_log_debug1(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: overriding non-expired items " "due to memory shortage for entry \"%V\"", &key); for (i = 0; i < 30; i++) { if (ngx_tcp_lua_shdict_expire(ctx, 0) == 0) { break; } forcible = 1; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node != NULL) { goto allocated; } } ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "no memory"); lua_pushboolean(L, forcible); return 3; } allocated: sd = (ngx_tcp_lua_shdict_node_t *) &node->color; node->key = hash; sd->key_len = (u_short) key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) (exptime * 1000); } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) value.len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key.data, key.len); ngx_memcpy(p, value.data, value.len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, forcible); return 3; }
static ngx_int_t ngx_http_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; }