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; }
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; }
static int ngx_tcp_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_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; 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_TCP, 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_tcp_lua_shdict_expire(ctx, 1); } #endif rc = ngx_tcp_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; }
static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache) { u_char *name; size_t len; time_t wait; ngx_uint_t tries; ngx_path_t *path; ngx_queue_t *q; ngx_http_file_cache_node_t *fcn; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache forced expire"); path = cache->path; len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; name = ngx_alloc(len + 1, ngx_cycle->log); if (name == NULL) { return 10; } ngx_memcpy(name, path->name.data, path->name.len); wait = 10; tries = 0; ngx_shmtx_lock(&cache->shpool->mutex); for (q = ngx_queue_last(&cache->sh->queue); q != ngx_queue_sentinel(&cache->sh->queue); q = ngx_queue_prev(q)) { fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd", fcn->count, fcn->exists, fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); if (fcn->count) { if (tries++ < 20) { continue; } wait = 1; break; } if (!fcn->exists) { ngx_queue_remove(q); ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); ngx_slab_free_locked(cache->shpool, fcn); break; } ngx_http_file_cache_delete(cache, q, name); break; } ngx_shmtx_unlock(&cache->shpool->mutex); ngx_free(name); return wait; }
static int ngx_tcp_lua_shdict_incr(lua_State *L) { int 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; double num; u_char *p; ngx_shm_zone_t *zone; double value; n = lua_gettop(L); if (n != 3) { return luaL_error(L, "expecting 3 arguments, but only seen %d", n); } if (lua_type(L, 1) != LUA_TLIGHTUSERDATA) { return luaL_error(L, "bad \"zone\" argument"); } 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; 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 = 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_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 (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(double)) { 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 = *(double *) p; num += value; ngx_memcpy(p, (double *) &num, sizeof(double)); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnumber(L, num); lua_pushnil(L); return 2; }
static void ngx_stream_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc, void *data) { ngx_stream_upstream_rr_peer_data_t *rrp = data; ngx_ssl_session_t *old_ssl_session, *ssl_session; ngx_stream_upstream_rr_peer_t *peer; #if (NGX_STREAM_UPSTREAM_ZONE) int len; u_char *p; ngx_stream_upstream_rr_peers_t *peers; u_char buf[NGX_SSL_MAX_SESSION_SIZE]; #endif #if (NGX_STREAM_UPSTREAM_ZONE) peers = rrp->peers; if (peers->shpool) { ssl_session = SSL_get0_session(pc->connection->ssl->connection); if (ssl_session == NULL) { return; } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, "save session: %p", ssl_session); len = i2d_SSL_SESSION(ssl_session, NULL); /* do not cache too big session */ if (len > NGX_SSL_MAX_SESSION_SIZE) { return; } p = buf; (void) i2d_SSL_SESSION(ssl_session, &p); peer = rrp->current; ngx_stream_upstream_rr_peers_rlock(peers); ngx_stream_upstream_rr_peer_lock(peers, peer); if (len > peer->ssl_session_len) { ngx_shmtx_lock(&peers->shpool->mutex); if (peer->ssl_session) { ngx_slab_free_locked(peers->shpool, peer->ssl_session); } peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len); ngx_shmtx_unlock(&peers->shpool->mutex); if (peer->ssl_session == NULL) { peer->ssl_session_len = 0; ngx_stream_upstream_rr_peer_unlock(peers, peer); ngx_stream_upstream_rr_peers_unlock(peers); return; } peer->ssl_session_len = len; } ngx_memcpy(peer->ssl_session, buf, len); ngx_stream_upstream_rr_peer_unlock(peers, peer); ngx_stream_upstream_rr_peers_unlock(peers); return; } #endif ssl_session = ngx_ssl_get_session(pc->connection); if (ssl_session == NULL) { return; } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, "save session: %p", ssl_session); peer = rrp->current; old_ssl_session = peer->ssl_session; peer->ssl_session = ssl_session; if (old_ssl_session) { ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, "old session: %p", old_ssl_session); /* TODO: may block */ ngx_ssl_free_session(old_ssl_session); } }
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) { 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_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit) { ngx_int_t excess; ngx_msec_t now, delay, max_delay; ngx_msec_int_t ms; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lr; excess = *ep; if (excess == 0 || (*limit)->nodelay) { max_delay = 0; } else { ctx = (*limit)->shm_zone->data; max_delay = excess * 1000 / ctx->rate; } while (n--) { ctx = limits[n].shm_zone->data; lr = ctx->node; if (lr == NULL) { continue; } ngx_shmtx_lock(&ctx->shpool->mutex); now = ngx_current_msec; ms = (ngx_msec_int_t) (now - lr->last); excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; if (excess < 0) { excess = 0; } lr->last = now; lr->excess = excess; lr->count--; ngx_shmtx_unlock(&ctx->shpool->mutex); ctx->node = NULL; if (limits[n].nodelay) { continue; } delay = excess * 1000 / ctx->rate; if (delay > max_delay) { max_delay = delay; *ep = excess; *limit = &limits[n]; } } return max_delay; }
static ngx_int_t ngx_http_sla_purge_handler (ngx_http_request_t* r) { ngx_uint_t i; size_t size; ngx_buf_t* buf; ngx_chain_t out; ngx_int_t result; ngx_str_t name; ngx_http_sla_pool_t* pool; ngx_http_sla_main_conf_t* config; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "sla_purge handler"); if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { return NGX_HTTP_NOT_ALLOWED; } result = ngx_http_discard_request_body(r); if (result != NGX_OK) { return result; } ngx_str_set(&r->headers_out.content_type, "text/plain"); if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; result = ngx_http_send_header(r); if (result == NGX_ERROR || result > NGX_OK || r->header_only) { return result; } } /* OK */ size = 4 * sizeof(u_char); buf = ngx_create_temp_buf(r->pool, size); if (buf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } out.buf = buf; out.next = NULL; buf->last = ngx_sprintf(buf->last, "OK\n"); config = ngx_http_get_module_main_conf(r, ngx_http_sla_module); /* сброс данных */ ngx_str_set(&name, "all"); pool = config->pools.elts; for (i = 0; i < config->pools.nelts; i++) { ngx_shmtx_lock(&pool->shm_pool->mutex); if (pool->generation == pool->shm_ctx->generation) { ngx_memzero(pool->shm_ctx, sizeof(ngx_http_sla_pool_shm_t) * NGX_HTTP_SLA_MAX_COUNTERS_LEN); pool->shm_ctx->generation = pool->generation; ngx_http_sla_add_counter(pool, &name, 0); } ngx_shmtx_unlock(&pool->shm_pool->mutex); pool++; } /* отправка результата */ r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = buf->last - buf->pos; buf->last_buf = (r == r->main) ? 1 : 0; result = ngx_http_send_header(r); if (result == NGX_ERROR || result > NGX_OK || r->header_only) { return result; } return ngx_http_output_filter(r, &out); }
static ngx_int_t ngx_http_sla_processor (ngx_http_request_t* r) { ngx_uint_t i; ngx_msec_int_t ms; ngx_msec_int_t time; ngx_uint_t status; ngx_str_t* alias; ngx_http_sla_pool_shm_t* counter; ngx_http_sla_loc_conf_t* config; ngx_http_upstream_state_t* state; config = ngx_http_get_module_loc_conf(r, ngx_http_sla_module); if (config->off != 0 || config->pool == NULL) { return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "sla processor"); ngx_shmtx_lock(&config->pool->shm_pool->mutex); if (config->pool->generation != config->pool->shm_ctx->generation) { ngx_shmtx_unlock(&config->pool->shm_pool->mutex); return NGX_OK; } /* суммарное время ответов апстримов */ time = 0; if (r->upstream_states != NULL && r->upstream_states->nelts > 0) { state = r->upstream_states->elts; for (i = 0; i < r->upstream_states->nelts; i++) { if (state[i].peer == NULL || state[i].status < 100 || state[i].status > 599) { continue; } ms = (ngx_msec_int_t)(state[i].response_sec * 1000 + state[i].response_msec); ms = ngx_max(ms, 0); time += ms; alias = ngx_http_sla_get_alias(config->aliases, state[i].peer); if (alias == NULL) { alias = state[i].peer; } counter = ngx_http_sla_get_counter(config->pool, alias); if (counter == NULL) { ngx_shmtx_unlock(&config->pool->shm_pool->mutex); return NGX_ERROR; } ngx_http_sla_set_http_time(config->pool, counter, ms); ngx_http_sla_set_http_status(config->pool, counter, state[i].status); } } /* пул и счетчик по умолчанию */ if (r->err_status) { status = r->err_status; } else if (r->headers_out.status) { status = r->headers_out.status; } else { status = 0; } ngx_http_sla_set_http_time(config->pool, config->pool->shm_ctx, time); ngx_http_sla_set_http_status(config->pool, config->pool->shm_ctx, status); ngx_shmtx_unlock(&config->pool->shm_pool->mutex); return NGX_OK; }
static ngx_int_t ngx_http_sla_status_handler (ngx_http_request_t* r) { ngx_uint_t i; size_t size; ngx_buf_t* buf; ngx_chain_t out; ngx_int_t result; ngx_http_sla_pool_t* pool; ngx_http_sla_main_conf_t* config; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "sla handler"); if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { return NGX_HTTP_NOT_ALLOWED; } result = ngx_http_discard_request_body(r); if (result != NGX_OK) { return result; } ngx_str_set(&r->headers_out.content_type, "text/plain"); if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; result = ngx_http_send_header(r); if (result == NGX_ERROR || result > NGX_OK || r->header_only) { return result; } } config = ngx_http_get_module_main_conf(r, ngx_http_sla_module); #ifndef NGX_HTTP_SLA_AIRBUG #define NGX_HTTP_SLA_AIRBUG 1024 #endif size = ( (sizeof("..http_xxx = ") + 2 * NGX_HTTP_SLA_MAX_NAME_LEN + NGX_ATOMIC_T_LEN + 1) * (NGX_HTTP_SLA_MAX_HTTP_LEN + 1 /* http_xxx */ + 6 /* http_2xx */) + (sizeof("..time.avg = ") + 2 * NGX_HTTP_SLA_MAX_NAME_LEN + NGX_ATOMIC_T_LEN + 1) + (sizeof("..time.avg.mov = ") + 2 * NGX_HTTP_SLA_MAX_NAME_LEN + NGX_ATOMIC_T_LEN + 1) + (sizeof(".. = ") + 2 * NGX_HTTP_SLA_MAX_NAME_LEN + 2 * NGX_ATOMIC_T_LEN + 1) * NGX_HTTP_SLA_MAX_TIMINGS_LEN + (sizeof("...agg = ") + 2 * NGX_HTTP_SLA_MAX_NAME_LEN + 2 * NGX_ATOMIC_T_LEN + 1) * NGX_HTTP_SLA_MAX_TIMINGS_LEN + (sizeof("..xx% = ") + 2 * NGX_HTTP_SLA_MAX_NAME_LEN + NGX_ATOMIC_T_LEN + 1) * NGX_HTTP_SLA_MAX_QUANTILES_LEN + 4 * NGX_HTTP_SLA_AIRBUG /* add two parachute, swiss knife and kit */ ) * NGX_HTTP_SLA_MAX_COUNTERS_LEN * config->pools.nelts; buf = ngx_create_temp_buf(r->pool, size); if (buf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } out.buf = buf; out.next = NULL; /* формирование результата */ pool = config->pools.elts; for (i = 0; i < config->pools.nelts; i++) { ngx_shmtx_lock(&pool->shm_pool->mutex); if (pool->generation == pool->shm_ctx->generation) { ngx_http_sla_print_pool(buf, pool); } ngx_shmtx_unlock(&pool->shm_pool->mutex); pool++; } /* отправка результата */ r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = buf->last - buf->pos; buf->last_buf = (r == r->main) ? 1 : 0; result = ngx_http_send_header(r); if (result == NGX_ERROR || result > NGX_OK || r->header_only) { return result; } return ngx_http_output_filter(r, &out); }
static ngx_int_t ngx_http_push_stream_subscriber_handler(ngx_http_request_t *r) { ngx_slab_pool_t *shpool = (ngx_slab_pool_t *)ngx_http_push_stream_shm_zone->shm.addr; ngx_http_push_stream_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_stream_module); ngx_http_push_stream_subscriber_t *worker_subscriber; ngx_http_push_stream_requested_channel_t *channels_ids, *cur; ngx_http_push_stream_subscriber_ctx_t *ctx; time_t if_modified_since; ngx_str_t *last_event_id, vv_time = ngx_null_string; ngx_str_t *push_mode; ngx_flag_t polling, longpolling; ngx_int_t rc; ngx_int_t status_code; ngx_str_t *explain_error_message; // add headers to support cross domain requests if (cf->allowed_origins.len > 0) { ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, &cf->allowed_origins); ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_METHODS, &NGX_HTTP_PUSH_STREAM_ALLOW_GET); ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, &NGX_HTTP_PUSH_STREAM_ALLOWED_HEADERS); } if (r->method & NGX_HTTP_OPTIONS) { return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_OK, NULL); } // only accept GET method if (!(r->method & NGX_HTTP_GET)) { ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ALLOW, &NGX_HTTP_PUSH_STREAM_ALLOW_GET); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_NOT_ALLOWED, NULL); } if ((ctx = ngx_http_push_stream_add_request_context(r)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to create request context"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } //get channels ids and backtracks from path channels_ids = ngx_http_push_stream_parse_channels_ids_from_path(r, ctx->temp_pool); if ((channels_ids == NULL) || ngx_queue_empty(&channels_ids->queue)) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push stream module: the $push_stream_channels_path variable is required but is not set"); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_BAD_REQUEST, &NGX_HTTP_PUSH_STREAM_NO_CHANNEL_ID_MESSAGE); } //validate channels: name, length and quantity. check if channel exists when authorized_channels_only is on. check if channel is full of subscribers if (ngx_http_push_stream_validate_channels(r, channels_ids, &status_code, &explain_error_message) == NGX_ERROR) { return ngx_http_push_stream_send_only_header_response(r, status_code, explain_error_message); } if (cf->last_received_message_time != NULL) { ngx_http_push_stream_complex_value(r, cf->last_received_message_time, &vv_time); } else if (r->headers_in.if_modified_since != NULL) { vv_time = r->headers_in.if_modified_since->value; } // get control headers if_modified_since = vv_time.len ? ngx_http_parse_time(vv_time.data, vv_time.len) : -1; last_event_id = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_LAST_EVENT_ID); push_mode = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_MODE); polling = ((cf->location_type == NGX_HTTP_PUSH_STREAM_SUBSCRIBER_MODE_POLLING) || ((push_mode != NULL) && (push_mode->len == NGX_HTTP_PUSH_STREAM_MODE_POLLING.len) && (ngx_strncasecmp(push_mode->data, NGX_HTTP_PUSH_STREAM_MODE_POLLING.data, NGX_HTTP_PUSH_STREAM_MODE_POLLING.len) == 0))); longpolling = ((cf->location_type == NGX_HTTP_PUSH_STREAM_SUBSCRIBER_MODE_LONGPOLLING) || ((push_mode != NULL) && (push_mode->len == NGX_HTTP_PUSH_STREAM_MODE_LONGPOLLING.len) && (ngx_strncasecmp(push_mode->data, NGX_HTTP_PUSH_STREAM_MODE_LONGPOLLING.data, NGX_HTTP_PUSH_STREAM_MODE_LONGPOLLING.len) == 0))); if (polling || longpolling) { ngx_int_t result = ngx_http_push_stream_subscriber_polling_handler(r, channels_ids, if_modified_since, last_event_id, longpolling, ctx->temp_pool); if (ctx->temp_pool != NULL) { ngx_destroy_pool(ctx->temp_pool); ctx->temp_pool = NULL; } return result; } ctx->padding = ngx_http_push_stream_get_padding_by_user_agent(r); // stream access if ((worker_subscriber = ngx_http_push_stream_subscriber_prepare_request_to_keep_connected(r)) == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_TRANSFER_ENCODING, &NGX_HTTP_PUSH_STREAM_HEADER_CHUNCKED); ngx_http_send_header(r); // sending response content header if (ngx_http_push_stream_send_response_content_header(r, cf) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, "push stream module: could not send content header to subscriber"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_shmtx_lock(&shpool->mutex); rc = ngx_http_push_stream_registry_subscriber_locked(r, worker_subscriber); ngx_shmtx_unlock(&shpool->mutex); if (rc == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } // adding subscriber to channel(s) and send backtrack messages cur = channels_ids; while ((cur = (ngx_http_push_stream_requested_channel_t *) ngx_queue_next(&cur->queue)) != channels_ids) { if (ngx_http_push_stream_subscriber_assign_channel(shpool, cf, r, cur, if_modified_since, last_event_id, worker_subscriber, ctx->temp_pool) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } if (ctx->temp_pool != NULL) { ngx_destroy_pool(ctx->temp_pool); ctx->temp_pool = NULL; } return NGX_DONE; }
static ngx_int_t ngx_http_push_stream_subscriber_polling_handler(ngx_http_request_t *r, ngx_http_push_stream_requested_channel_t *channels_ids, time_t if_modified_since, ngx_str_t *last_event_id, ngx_flag_t longpolling, ngx_pool_t *temp_pool) { ngx_http_push_stream_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_stream_module); ngx_slab_pool_t *shpool = (ngx_slab_pool_t *)ngx_http_push_stream_shm_zone->shm.addr; ngx_http_push_stream_subscriber_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_push_stream_module); ngx_http_push_stream_requested_channel_t *cur; ngx_http_push_stream_subscriber_t *worker_subscriber; ngx_http_push_stream_channel_t *channel; ngx_http_push_stream_subscription_t *subscription; ngx_str_t *etag = NULL, vv_etag = ngx_null_string; ngx_int_t tag; time_t greater_message_time; ngx_int_t greater_message_tag; ngx_flag_t has_message_to_send = 0; ngx_str_t callback_function_name; if (cf->last_received_message_tag != NULL) { ngx_http_push_stream_complex_value(r, cf->last_received_message_tag, &vv_etag); etag = vv_etag.len ? &vv_etag : NULL; } else { etag = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_IF_NONE_MATCH); } if (ngx_http_arg(r, NGX_HTTP_PUSH_STREAM_CALLBACK.data, NGX_HTTP_PUSH_STREAM_CALLBACK.len, &callback_function_name) == NGX_OK) { ngx_http_push_stream_unescape_uri(&callback_function_name); if ((ctx->callback = ngx_http_push_stream_get_formatted_chunk(callback_function_name.data, callback_function_name.len, r->pool)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory for callback function name"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } } tag = ((etag != NULL) && ((tag = ngx_atoi(etag->data, etag->len)) != NGX_ERROR)) ? ngx_abs(tag) : -1; greater_message_tag = tag; greater_message_time = if_modified_since = (if_modified_since < 0) ? 0 : if_modified_since; ngx_shmtx_lock(&shpool->mutex); // check if has any message to send cur = channels_ids; while ((cur = (ngx_http_push_stream_requested_channel_t *) ngx_queue_next(&cur->queue)) != channels_ids) { channel = ngx_http_push_stream_find_channel(cur->id, r->connection->log); if (channel == NULL) { // channel not found ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate shared memory for channel %s", cur->id->data); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (ngx_http_push_stream_has_old_messages_to_send(channel, cur->backtrack_messages, if_modified_since, tag, greater_message_time, greater_message_tag, last_event_id)) { has_message_to_send = 1; if (channel->last_message_time > greater_message_time) { greater_message_time = channel->last_message_time; greater_message_tag = channel->last_message_tag; } else { if ((channel->last_message_time == greater_message_time) && (channel->last_message_tag > greater_message_tag) ) { greater_message_tag = channel->last_message_tag; } } } } if (longpolling && !has_message_to_send) { // long polling mode without messages if ((worker_subscriber = ngx_http_push_stream_subscriber_prepare_request_to_keep_connected(r)) == NULL) { ngx_shmtx_unlock(&shpool->mutex); return NGX_HTTP_INTERNAL_SERVER_ERROR; } worker_subscriber->longpolling = 1; if (ngx_http_push_stream_registry_subscriber_locked(r, worker_subscriber) == NGX_ERROR) { ngx_shmtx_unlock(&shpool->mutex); return NGX_HTTP_INTERNAL_SERVER_ERROR; } // adding subscriber to channel(s) cur = channels_ids; while ((cur = (ngx_http_push_stream_requested_channel_t *) ngx_queue_next(&cur->queue)) != channels_ids) { if ((channel = ngx_http_push_stream_find_channel(cur->id, r->connection->log)) == NULL) { // channel not found ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate shared memory for channel %s", cur->id->data); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if ((subscription = ngx_http_push_stream_create_channel_subscription(r, channel, worker_subscriber)) == NULL) { ngx_shmtx_unlock(&shpool->mutex); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_push_stream_assing_subscription_to_channel_locked(shpool, cur->id, subscription, &worker_subscriber->subscriptions_sentinel, r->connection->log); } ngx_shmtx_unlock(&shpool->mutex); return NGX_DONE; } ngx_shmtx_unlock(&shpool->mutex); // polling or long polling without messages to send ngx_http_push_stream_add_polling_headers(r, greater_message_time, greater_message_tag, temp_pool); if (!has_message_to_send) { // polling subscriber requests get a 304 with their entity tags preserved if don't have new messages. return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_NOT_MODIFIED, NULL); } // polling with messages or long polling without messages to send r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = -1; ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_TRANSFER_ENCODING, &NGX_HTTP_PUSH_STREAM_HEADER_CHUNCKED); ngx_http_send_header(r); // sending response content header if (ngx_http_push_stream_send_response_content_header(r, cf) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, "push stream module: could not send content header to subscriber"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (ctx->callback != NULL) { ngx_http_push_stream_send_response_text(r, ctx->callback->data, ctx->callback->len, 0); ngx_http_push_stream_send_response_text(r, NGX_HTTP_PUSH_STREAM_CALLBACK_INIT_CHUNK.data, NGX_HTTP_PUSH_STREAM_CALLBACK_INIT_CHUNK.len, 0); } cur = channels_ids; while ((cur = (ngx_http_push_stream_requested_channel_t *) ngx_queue_next(&cur->queue)) != channels_ids) { channel = ngx_http_push_stream_find_channel(cur->id, r->connection->log); if (channel == NULL) { // channel not found ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate shared memory for channel %s", cur->id->data); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_push_stream_send_old_messages(r, channel, cur->backtrack_messages, if_modified_since, tag, greater_message_time, greater_message_tag, last_event_id); } if (ctx->callback != NULL) { ngx_http_push_stream_send_response_text(r, NGX_HTTP_PUSH_STREAM_CALLBACK_END_CHUNK.data, NGX_HTTP_PUSH_STREAM_CALLBACK_END_CHUNK.len, 0); } if (cf->footer_template.len > 0) { ngx_http_push_stream_send_response_text(r, cf->footer_template.data, cf->footer_template.len, 0); } ngx_http_push_stream_send_response_text(r, NGX_HTTP_PUSH_STREAM_LAST_CHUNK.data, NGX_HTTP_PUSH_STREAM_LAST_CHUNK.len, 1); return NGX_OK; }
void ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf) { ngx_http_file_cache_t *cache; ngx_http_file_cache_node_t *fcn; if (c->updated || c->node == NULL) { return; } cache = c->file_cache; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0, "http file cache free, fd: %d", c->file.fd); ngx_shmtx_lock(&cache->shpool->mutex); fcn = c->node; fcn->count--; if (c->updating) { fcn->updating = 0; } if (c->error) { fcn->error = c->error; if (c->valid_sec) { fcn->valid_sec = c->valid_sec; fcn->valid_msec = c->valid_msec; } } else if (!fcn->exists && fcn->count == 0 && c->min_uses == 1) { ngx_queue_remove(&fcn->queue); ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); ngx_slab_free_locked(cache->shpool, fcn); c->node = NULL; } ngx_shmtx_unlock(&cache->shpool->mutex); c->updated = 1; c->updating = 0; if (c->temp_file) { if (tf && tf->file.fd != NGX_INVALID_FILE) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0, "http file cache incomplete: \"%s\"", tf->file.name.data); if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno, ngx_delete_file_n " \"%s\" failed", tf->file.name.data); } } } if (c->wait_event.timer_set) { ngx_del_timer(&c->wait_event); } }
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; }
ngx_int_t ngx_http_file_cache_purge(ngx_http_request_t *r) { ngx_http_file_cache_t *cache; ngx_http_cache_t *c; switch (ngx_http_file_cache_open(r)) { case NGX_OK: case NGX_HTTP_CACHE_STALE: # if (nginx_version >= 8001) \ || ((nginx_version < 8000) && (nginx_version >= 7060)) case NGX_HTTP_CACHE_UPDATING: # endif break; case NGX_DECLINED: return NGX_DECLINED; # if (NGX_HAVE_FILE_AIO) case NGX_AGAIN: return NGX_AGAIN; # endif default: return NGX_ERROR; } c = r->cache; cache = c->file_cache; /* * delete file from disk but *keep* in-memory node, * because other requests might still point to it. */ ngx_shmtx_lock(&cache->shpool->mutex); if (!c->node->exists) { /* race between concurrent purges, backoff */ ngx_shmtx_unlock(&cache->shpool->mutex); return NGX_DECLINED; } # if (nginx_version >= 1000001) cache->sh->size -= c->node->fs_size; c->node->fs_size = 0; # else cache->sh->size -= (c->node->length + cache->bsize - 1) / cache->bsize; c->node->length = 0; # endif c->node->exists = 0; # if (nginx_version >= 8001) \ || ((nginx_version < 8000) && (nginx_version >= 7060)) c->node->updating = 0; # endif ngx_shmtx_unlock(&cache->shpool->mutex); if (ngx_delete_file(c->file.name.data) == NGX_FILE_ERROR) { /* entry in error log is enough, don't notice client */ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, ngx_delete_file_n " \"%s\" failed", c->file.name.data); } /* file deleted from cache */ return NGX_OK; }
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; }
static ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c) { time_t now; ssize_t n; ngx_int_t rc; ngx_http_file_cache_t *cache; ngx_http_file_cache_header_t *h; c = r->cache; n = ngx_http_file_cache_aio_read(r, c); if (n < 0) { return n; } if ((size_t) n < c->header_start) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, "cache file \"%s\" is too small", c->file.name.data); return NGX_ERROR; } h = (ngx_http_file_cache_header_t *) c->buf->pos; if (h->crc32 != c->crc32) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, "cache file \"%s\" has md5 collision", c->file.name.data); return NGX_DECLINED; } c->buf->last += n; c->valid_sec = h->valid_sec; c->last_modified = h->last_modified; c->date = h->date; c->valid_msec = h->valid_msec; c->header_start = h->header_start; c->body_start = h->body_start; r->cached = 1; cache = c->file_cache; if (cache->sh->cold) { ngx_shmtx_lock(&cache->shpool->mutex); if (!c->node->exists) { c->node->uses = 1; c->node->body_start = c->body_start; c->node->exists = 1; c->node->uniq = c->uniq; cache->sh->size += (c->length + cache->bsize - 1) / cache->bsize; } ngx_shmtx_unlock(&cache->shpool->mutex); } now = ngx_time(); if (c->valid_sec < now) { ngx_shmtx_lock(&cache->shpool->mutex); if (c->node->updating) { rc = NGX_HTTP_CACHE_UPDATING; } else { c->node->updating = 1; rc = NGX_HTTP_CACHE_STALE; } ngx_shmtx_unlock(&cache->shpool->mutex); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http file cache expired: %i %T %T", rc, c->valid_sec, now); return rc; } return NGX_OK; }
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; }
void ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf) { off_t size, length; ngx_int_t rc; ngx_file_uniq_t uniq; ngx_file_info_t fi; ngx_http_cache_t *c; ngx_ext_rename_file_t ext; ngx_http_file_cache_t *cache; c = r->cache; if (c->updated) { return; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http file cache update"); c->updated = 1; cache = c->file_cache; uniq = 0; length = 0; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http file cache rename: \"%s\" to \"%s\"", tf->file.name.data, c->file.name.data); ext.access = NGX_FILE_OWNER_ACCESS; ext.path_access = NGX_FILE_OWNER_ACCESS; ext.time = -1; ext.create_path = 1; ext.delete_file = 1; ext.log = r->connection->log; rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext); if (rc == NGX_OK) { if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, ngx_fd_info_n " \"%s\" failed", tf->file.name.data); rc = NGX_ERROR; } else { uniq = ngx_file_uniq(&fi); length = ngx_file_size(&fi); } } size = (length + cache->bsize - 1) / cache->bsize; ngx_shmtx_lock(&cache->shpool->mutex); c->node->count--; c->node->uniq = uniq; c->node->body_start = c->body_start; size = size - (c->node->length + cache->bsize - 1) / cache->bsize; c->node->length = length; cache->sh->size += size; if (rc == NGX_OK) { c->node->exists = 1; } c->node->updating = 0; ngx_shmtx_unlock(&cache->shpool->mutex); }
static ngx_int_t ngx_http_ip_blacklist_show_handler(ngx_http_request_t *r) { ngx_int_t rc, j = 0; ngx_buf_t *b; ngx_chain_t out; ngx_str_t *test; ngx_http_ip_blacklist_main_conf_t *imcf; const char *banner = "IP blacklist is not enabled<br>"; const char *empty = "IP blacklist is empty<br>"; ngx_http_ip_blacklist_tree_t *blacklist; ngx_http_ip_blacklist_t *bn; ngx_queue_t *node; char tmp[NGX_HTTP_IP_BLACKLIST_ADDR_LEN]; ngx_uint_t total = 0; imcf = ngx_http_get_module_main_conf(r, ngx_http_ip_blacklist_module); test = ngx_pcalloc(r->pool, sizeof(ngx_str_t)); if (!test) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } test->data = ngx_pcalloc(r->pool, imcf->size * sizeof(ngx_http_ip_blacklist_t) + 1024); if (!test->data) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (!imcf->enabled) { memcpy(test->data, banner, strlen(banner)); test->len = strlen(banner); goto not_enabled; } else { memcpy(test->data, "Total: <br>IP Address(es): <br>", strlen("Total: <br>IP Address(es): <br>")); test->len = strlen("Total: <br>IP Address(es): <br>"); } blacklist = ngx_http_ip_blacklist_shm_zone->data; ngx_shmtx_lock(&blacklist->shpool->mutex); if (ngx_queue_empty(&blacklist->garbage)) { ngx_shmtx_unlock(&blacklist->shpool->mutex); memcpy(test->data, empty, strlen(empty)); test->len = strlen(empty); goto empty; } for (node = ngx_queue_head(&blacklist->garbage); node != ngx_queue_sentinel(&blacklist->garbage); node = ngx_queue_next(node)) { bn = ngx_queue_data(node, ngx_http_ip_blacklist_t, queue); if (bn->blacklist) { memset(tmp, 0, NGX_HTTP_IP_BLACKLIST_ADDR_LEN); memcpy(tmp, bn->addr, bn->len < NGX_HTTP_IP_BLACKLIST_ADDR_LEN ? bn->len : NGX_HTTP_IP_BLACKLIST_ADDR_LEN - 1); j = sprintf((char *)(test->data + test->len), "addr: %s, timeout: %d, " "timed out: %d, blacklist: %d, ref: %d <br>", tmp, (int)(bn->timeout - ngx_time()), bn->timed, bn->blacklist, (int)bn->ref); test->len += j; total++; } } sprintf((char *)(test->data + 7), "%u", (unsigned int)total); ngx_shmtx_unlock(&blacklist->shpool->mutex); empty: not_enabled: b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } out.buf = b; out.next = NULL; b->pos = test->data; b->last = test->data + test->len; b->memory = 1; b->last_buf = 1; r->headers_out.content_type.len = sizeof("text/html") - 1; r->headers_out.content_type.data = (u_char *) "text/html"; r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = test->len; rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } return ngx_http_output_filter(r, &out); }
static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache) { u_char *name, *p; size_t len; time_t now, wait; ngx_path_t *path; ngx_queue_t *q; ngx_http_file_cache_node_t *fcn; u_char key[2 * NGX_HTTP_CACHE_KEY_LEN]; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache expire"); path = cache->path; len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; name = ngx_alloc(len + 1, ngx_cycle->log); if (name == NULL) { return 10; } ngx_memcpy(name, path->name.data, path->name.len); now = ngx_time(); ngx_shmtx_lock(&cache->shpool->mutex); for ( ;; ) { if (ngx_queue_empty(&cache->sh->queue)) { wait = 10; break; } q = ngx_queue_last(&cache->sh->queue); fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); wait = fcn->expire - now; if (wait > 0) { wait = wait > 10 ? 10 : wait; break; } ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache expire: #%d %d %02xd%02xd%02xd%02xd", fcn->count, fcn->exists, fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); if (fcn->count) { p = ngx_hex_dump(key, (u_char *) &fcn->node.key, sizeof(ngx_rbtree_key_t)); len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t); (void) ngx_hex_dump(p, fcn->key, len); /* * abnormally exited workers may leave locked cache entries, * and although it may be safe to remove them completely, * we prefer to remove them from inactive queue and rbtree * only, and to allow other leaks */ ngx_queue_remove(q); ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "ignore long locked inactive cache entry %*s, count:%d", 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count); continue; } if (!fcn->exists) { ngx_queue_remove(q); ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); ngx_slab_free_locked(cache->shpool, fcn); continue; } ngx_http_file_cache_delete(cache, q, name); } ngx_shmtx_unlock(&cache->shpool->mutex); ngx_free(name); return wait; }
/* * 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; }
ngx_int_t ngx_tcp_lua_shared_dict_get(ngx_shm_zone_t *zone, u_char *key_data, size_t key_len, ngx_tcp_lua_value_t *value) { u_char *data; size_t len; uint32_t hash; ngx_int_t rc; ngx_tcp_lua_shdict_ctx_t *ctx; ngx_tcp_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_tcp_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"); ngx_shmtx_unlock(&ctx->shpool->mutex); 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 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; }
int ngx_tcp_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_tcp_lua_shdict_ctx_t *ctx; ngx_tcp_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_TCP, 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_tcp_lua_shdict_expire(ctx, 1); } #endif rc = ngx_tcp_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_TBOOLEAN) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_ERROR; } if (*value_type == LUA_TSTRING) { *str_value_buf = malloc(value.len); if (*str_value_buf == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_ERROR; } } } switch (*value_type) { case LUA_TSTRING: *str_value_len = value.len; 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; } *str_value_len = value.len; *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); return NGX_ERROR; } *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 int ngx_http_lua_shdict_set_helper(lua_State *L, int flags) { 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; key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { return luaL_error(L, "attempt 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, 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 = key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + 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, 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) { 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\"", &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 = (uint64_t) tp->sec * 1000 + tp->msec + 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_tcp_lua_shdict_flush_expired(lua_State *L) { ngx_queue_t *q, *prev; ngx_tcp_lua_shdict_node_t *sd; ngx_tcp_lua_shdict_ctx_t *ctx; ngx_shm_zone_t *zone; ngx_time_t *tp; int freed = 0; int attempts = 0; ngx_rbtree_node_t *node; uint64_t now; int n; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting 1 or 2 argument(s), but saw %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"); } if (n == 2) { attempts = luaL_checkint(L, 2); } ctx = zone->data; ngx_shmtx_lock(&ctx->shpool->mutex); if (ngx_queue_empty(&ctx->sh->queue)) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnumber(L, 0); return 1; } tp = ngx_timeofday(); now = (uint64_t) tp->sec * 1000 + tp->msec; q = ngx_queue_last(&ctx->sh->queue); while (q != ngx_queue_sentinel(&ctx->sh->queue)) { prev = ngx_queue_prev(q); sd = ngx_queue_data(q, ngx_tcp_lua_shdict_node_t, queue); if (sd->expires != 0 && sd->expires <= now) { ngx_queue_remove(q); 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); freed++; if (attempts && freed == attempts) { break; } } q = prev; } ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnumber(L, freed); return 1; }
void ngx_process_events_and_timers(ngx_cycle_t *cycle) { ngx_uint_t flags; ngx_msec_t timer, delta; if (ngx_timer_resolution) { timer = NGX_TIMER_INFINITE; flags = 0; } else { timer = ngx_event_find_timer(); flags = NGX_UPDATE_TIME; #if (NGX_THREADS) if (timer == NGX_TIMER_INFINITE || timer > 500) { timer = 500; } #endif } if (ngx_use_accept_mutex) { if (ngx_accept_disabled > 0) { ngx_accept_disabled--; } else { if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { return; } if (ngx_accept_mutex_held) { flags |= NGX_POST_EVENTS; } else { if (timer == NGX_TIMER_INFINITE || timer > ngx_accept_mutex_delay) { timer = ngx_accept_mutex_delay; } } } } delta = ngx_current_msec; (void) ngx_process_events(cycle, timer, flags); delta = ngx_current_msec - delta; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "timer delta: %M", delta); if (ngx_posted_accept_events) { ngx_event_process_posted(cycle, &ngx_posted_accept_events); } if (ngx_accept_mutex_held) { ngx_shmtx_unlock(&ngx_accept_mutex); } if (delta) { ngx_event_expire_timers(); } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "posted events %p", ngx_posted_events); if (ngx_posted_events) { if (ngx_threaded) { ngx_wakeup_worker_thread(cycle); } else { ngx_event_process_posted(cycle, &ngx_posted_events); } } }