static ngx_int_t ngx_conf_add_dump(ngx_conf_t *cf, ngx_str_t *filename) { off_t size; u_char *p; uint32_t hash; ngx_buf_t *buf; ngx_str_node_t *sn; ngx_conf_dump_t *cd; hash = ngx_crc32_long(filename->data, filename->len); sn = ngx_str_rbtree_lookup(&cf->cycle->config_dump_rbtree, filename, hash); if (sn) { cf->conf_file->dump = NULL; return NGX_OK; } p = ngx_pstrdup(cf->cycle->pool, filename); if (p == NULL) { return NGX_ERROR; } cd = ngx_array_push(&cf->cycle->config_dump); if (cd == NULL) { return NGX_ERROR; } size = ngx_file_size(&cf->conf_file->file.info); buf = ngx_create_temp_buf(cf->cycle->pool, (size_t) size); if (buf == NULL) { return NGX_ERROR; } cd->name.data = p; cd->name.len = filename->len; cd->buffer = buf; cf->conf_file->dump = buf; sn = ngx_palloc(cf->temp_pool, sizeof(ngx_str_node_t)); if (sn == NULL) { return NGX_ERROR; } sn->node.key = hash; sn->str = cd->name; ngx_rbtree_insert(&cf->cycle->config_dump_rbtree, &sn->node); return NGX_OK; }
/* The md5 hash vaule has better random value with the input string than crc32. * And the virtual node can be dispersed uniformly in the ring. * */ static int32_t ngx_http_upstream_consistent_hash_node_point(u_char *str, size_t len) { u_char md5_buf[16]; ngx_md5_t md5; ngx_md5_init(&md5); ngx_md5_update(&md5, str, len); ngx_md5_final(md5_buf, &md5); return ngx_crc32_long(md5_buf, 16); }
static int ngx_http_lua_ngx_crc32_long(lua_State *L) { u_char *p; size_t len; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument, but got %d", lua_gettop(L)); } p = (u_char *) luaL_checklstring(L, 1, &len); lua_pushnumber(L, (lua_Number) ngx_crc32_long(p, len)); return 1; }
ngx_inline ngx_array_t * yy_sec_waf_re_cache_get_value(ngx_rbtree_t *rbtree, ngx_str_t *name) { uint32_t hash; re_cache_node_t *cache_node; hash = ngx_crc32_long(name->data, name->len); cache_node = (re_cache_node_t *) ngx_str_rbtree_lookup(rbtree, name, hash); if (cache_node != NULL) { return cache_node->value; } return NULL; }
static ngx_int_t ngx_http_upstream_init_q_chash_peer(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us) { ngx_int_t rc; ngx_http_upstream_q_chash_srv_conf_t *uchscf; ngx_http_upstream_q_chash_peer_data_t *qchp; ngx_http_upstream_q_chash_ring *q_chash_ring; ngx_str_t evaluated_key_to_hash; uchscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_q_chash_module); if (uchscf == NULL) { return NGX_ERROR; } q_chash_ring = uchscf->q_chash_ring; qchp = ngx_pcalloc(r->pool, sizeof(*qchp)); if(qchp == NULL) return NGX_ERROR; r->upstream->peer.data = &qchp->rrp; qchp->q_chash_ring = q_chash_ring; qchp->get_rr_peer = ngx_http_upstream_get_round_robin_peer; qchp->tries = 0; qchp->ignore = 0; qchp->rr_mode = 0; rc = ngx_http_upstream_init_round_robin_peer(r, us); if(rc != NGX_OK) return NGX_ERROR; r->upstream->peer.get = ngx_http_upstream_get_q_chash_peer; // calculate the vnode_index if(q_chash_ring->nr_valid_peers > 1) { if (ngx_http_script_run(r, &evaluated_key_to_hash, uchscf->lengths->elts, 0, uchscf->values->elts) == NULL) return NGX_ERROR; qchp->point = (uint32_t)ngx_crc32_long(evaluated_key_to_hash.data, evaluated_key_to_hash.len); qchp->vnode_index = q_chash_find(q_chash_ring, qchp->point); ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "q_chash key %V, point %uD, vnode_index %ui", &evaluated_key_to_hash, qchp->point, qchp->vnode_index); } return NGX_OK; }
static ngx_int_t ngx_http_upstream_init_consistent_hash_peer(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us) { ngx_str_t evaluated_key_to_hash; ngx_http_upstream_consistent_hash_srv_conf_t *uchscf; ngx_http_upstream_consistent_hash_peer_data_t *uchpd; uchscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_consistent_hash_module); if (uchscf == NULL) { return NGX_ERROR; } uchpd = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_consistent_hash_peer_data_t)); if (uchpd == NULL) { return NGX_ERROR; } r->upstream->peer.data = &uchpd->rrp; if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) { return NGX_ERROR; } r->upstream->peer.get = ngx_http_upstream_get_consistent_hash_peer; uchpd->buckets = uchscf->data; uchpd->tries = 0; if (ngx_http_script_run(r, &evaluated_key_to_hash, uchscf->lengths->elts, 0, uchscf->values->elts) == NULL) { return NGX_ERROR; } uchpd->point = ngx_crc32_long(evaluated_key_to_hash.data, evaluated_key_to_hash.len); uchpd->get_rr_peer = ngx_http_upstream_get_round_robin_peer; return NGX_OK; }
ngx_int_t yy_sec_waf_re_cache_set_value(ngx_pool_t *pool, ngx_str_t *name, ngx_array_t *value, ngx_rbtree_t *rbtree) { uint32_t hash; ngx_str_t *val; re_cache_node_t *cache_node; hash = ngx_crc32_long(name->data, name->len); cache_node = (re_cache_node_t *) ngx_str_rbtree_lookup(rbtree, name, hash); if (cache_node != NULL) { return NGX_OK; } cache_node = ngx_palloc(pool, sizeof(re_cache_node_t)); if (cache_node == NULL) { return NGX_ERROR; } val = ngx_palloc(pool, sizeof(ngx_str_t)); if (value == NULL) { return NGX_ERROR; } val->len = value->len; val->data = ngx_pstrdup(pool, value); if (val->data == NULL) { return NGX_ERROR; } cache_node->sn.node.key = hash; cache_node->sn.str.len = name->len; cache_node->sn.str.data = name->data; cache_node->value = value; ngx_rbtree_insert(rbtree, &cache_node->sn.node); return NGX_OK; }
static ngx_int_t ngx_http_crc32_hash_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_http_hash_ctx_t *ctx = (ngx_http_hash_ctx_t *)data; ngx_str_t val; if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) { return NGX_ERROR; } v->len = sizeof("3C32515A") - 1; if (v->len <= ctx->start) { return NGX_ERROR; } if (v->len < ctx->end) { return NGX_ERROR; } v->data = ngx_palloc(r->pool, v->len); if (v->data == NULL) { v->not_found = 1; return NGX_OK; } ngx_sprintf(v->data, "%08XD", ngx_crc32_long(val.data, val.len)); v->valid = 1; v->not_found = 0; v->no_cacheable = 0; if (ctx->end) { v->data += ctx->start; v->len = ctx->end; } return NGX_OK; }
static ngx_int_t ngx_http_upstream_init_consistent_hash_peer(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us) { ngx_str_t evaluated_key_to_hash; ngx_http_upstream_consistent_hash_srv_conf_t *uchscf; ngx_http_upstream_consistent_hash_peer_data_t *uchpd; uchscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_consistent_hash_module); if (uchscf == NULL) { return NGX_ERROR; } uchpd = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_consistent_hash_peer_data_t)); if (uchpd == NULL) { return NGX_ERROR; } r->upstream->peer.data = uchpd->peers; uchpd->peers = us->peer.data; if (ngx_http_script_run(r, &evaluated_key_to_hash, uchscf->lengths->elts, 0, uchscf->values->elts) == NULL) { return NGX_ERROR; } uchpd->point = ngx_crc32_long(evaluated_key_to_hash.data, evaluated_key_to_hash.len); r->upstream->peer.free = ngx_http_upstream_free_consistent_hash_peer; r->upstream->peer.get = ngx_http_upstream_get_consistent_hash_peer; r->upstream->peer.data = uchpd; return NGX_OK; }
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; }
ngx_int_t ngx_http_upstream_init_consistent_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) { /* ip max 15, :port max 6, maxweight is highest number of uchar */ u_char hash_data[HASH_DATA_LENGTH]; uint32_t step; ngx_uint_t i, j, k, n, points = 0; ngx_http_upstream_server_t *server; ngx_http_upstream_consistent_hash_buckets *buckets; ngx_http_upstream_consistent_hash_continuum *continuum; for (i=0;i<HASH_DATA_LENGTH;i++) hash_data[i] = 0; step = (uint32_t) (0xffffffff / MMC_CONSISTENT_BUCKETS); buckets = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_consistent_hash_buckets)); us->peer.init = ngx_http_upstream_init_consistent_hash_peer; if (!us->servers) { return NGX_ERROR; } server = us->servers->elts; for (n = 0, i = 0; i < us->servers->nelts; i++) { n += server[i].naddrs; points += server[i].weight * server[i].naddrs * MMC_CONSISTENT_POINTS; } continuum = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_consistent_hash_continuum)); continuum->nodes = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_consistent_hash_node) * points); for (i = 0; i < us->servers->nelts; i++) { for (j = 0; j < server[i].naddrs; j++) { for (k = 0; k < ((MMC_CONSISTENT_POINTS * server[i].weight) / server[i].naddrs); k++) { ngx_snprintf(hash_data, 28, "%V-%ui", &server[i].addrs[j].name, k); continuum->nodes[continuum->nnodes].sockaddr = server[i].addrs[j].sockaddr; continuum->nodes[continuum->nnodes].socklen = server[i].addrs[j].socklen; continuum->nodes[continuum->nnodes].name = server[i].addrs[j].name; continuum->nodes[continuum->nnodes].name.data[server[i].addrs[j].name.len] = 0; continuum->nodes[continuum->nnodes].point = ngx_crc32_long(hash_data, ngx_strlen(hash_data)); continuum->nnodes++; } } } qsort(continuum->nodes, continuum->nnodes, sizeof(ngx_http_upstream_consistent_hash_node), (const void*) ngx_http_upstream_consistent_hash_compare_continuum_nodes); for (i = 0; i < MMC_CONSISTENT_BUCKETS; i++) { buckets->buckets[i] = ngx_http_upstream_consistent_hash_find(continuum, step * i); } #if (CONSISTENT_DEBUG) ngx_http_upstream_consistent_hash_print_continuum(cf, continuum); ngx_http_upstream_consistent_hash_print_buckets(cf, buckets); #endif buckets->continuum = continuum; us->peer.data = buckets; return NGX_OK; }
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; }
// make our proposed ZIP-file chunk map ngx_int_t ngx_http_zip_generate_pieces(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx) { ngx_uint_t i, piece_i; off_t offset = 0; time_t unix_time = 0; ngx_uint_t dos_time = 0; ngx_http_zip_file_t *file; ngx_http_zip_piece_t *header_piece, *file_piece, *trailer_piece, *cd_piece; ngx_http_variable_value_t *vv; if ((vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t))) == NULL) return NGX_ERROR; ctx->unicode_path = 0; #ifdef NGX_ZIP_HAVE_ICONV iconv_t *iconv_cd = NULL; #endif // Let's try to find special header that contains separator string. // What for this strange separator string you ask? // Sometimes there might be a problem converting UTF-8 to zips native // charset(CP866), because it's not 1:1 conversion. So my solution is to // developers provide their own version of converted filename and pass it // to mod_zip along with UTF-8 filename which will go straight to Unicode // path extra field (thanks to tony2001). So separator is a solution that doesn't // break current format. And allows passing file name in both formats as one string. // // Normally we pass: // CRC32 <size> <path> <filename>\n // ... // * <filename> passed to archive as filename w/o conversion // * UFT-8 flag for filename is set // // tony2001's X-Archive-Charset: <charset> way: // CRC32 <size> <path> <filename>\n // ... // * <filename> is accepted to be UTF-8 string // * <filename>, converted to <charset> and passed to archive as filename // * <filename> passed to Unicode path extra field // * UFT-8 flag for filename is not set // // My X-Archive-Name-Sep: <sep> solution: // CRC32 <size> <path> <native-filename><sep><utf8-filename>\n // ... // * <native-filename> passed to archive as filename w/o conversion // * <utf8-filename> passed to Unicode path extra field // * UFT-8 flag for filename is not set // // You just need to provide separator that won't interfere with file names. I suggest using '/' // as it is ASCII character and forbidden on most (if not all) platforms as a part of filename. // // Empty separator string means no UTF-8 version provided. Usefull when we need to pass only // names encoded in native charset. It's equal to 'X-Archive-Charset: native;'. // Note: Currently it is impossible after '[PATCH] Support for UTF-8 file names.'(4f61592b) // because UFT-8 flag (zip_utf8_flag) is set default for templates. if(ngx_http_upstream_header_variable(r, vv, (uintptr_t)(&ngx_http_zip_header_name_separator)) == NGX_OK && !vv->not_found) { ctx->native_charset = 1; if(vv->len) ctx->unicode_path = 1; } else { #ifdef NGX_ZIP_HAVE_ICONV if (ngx_http_upstream_header_variable(r, vv, (uintptr_t)(&ngx_http_zip_header_charset_name)) == NGX_OK && !vv->not_found && ngx_strncmp(vv->data, "utf8", sizeof("utf8") - 1) != 0) { if(ngx_strncmp(vv->data, "native", sizeof("native") - 1)) { char encoding[ICONV_CSNMAXLEN]; snprintf(encoding, sizeof(encoding), "%s//TRANSLIT//IGNORE", vv->data); iconv_cd = iconv_open((const char *)encoding, "utf-8"); if (iconv_cd == (iconv_t)(-1)) { ngx_log_error(NGX_LOG_WARN, r->connection->log, errno, "mod_zip: iconv_open('%s', 'utf-8') failed", vv->data); iconv_cd = NULL; } else { ctx->unicode_path = 1; ctx->native_charset = 1; } } else ctx->native_charset = 1; } #endif } // pieces: for each file: header, data, footer (if needed) -> 2 or 3 per file // plus file footer (CD + [zip64 end + zip64 locator +] end of cd) in one chunk ctx->pieces_n = ctx->files.nelts * (2 + (!!ctx->missing_crc32)) + 1; if ((ctx->pieces = ngx_palloc(r->pool, sizeof(ngx_http_zip_piece_t) * ctx->pieces_n)) == NULL) return NGX_ERROR; ctx->cd_size = 0; unix_time = time(NULL); dos_time = ngx_dos_time(unix_time); for (piece_i = i = 0; i < ctx->files.nelts; i++) { file = &((ngx_http_zip_file_t *)ctx->files.elts)[i]; file->offset = offset; file->unix_time = unix_time; file->dos_time = dos_time; if(ctx->unicode_path) { #ifdef NGX_ZIP_HAVE_ICONV if (iconv_cd) { size_t inlen = file->filename.len, outlen, outleft; u_char *p, *in; //inbuf file->filename_utf8.data = ngx_pnalloc(r->pool, file->filename.len + 1); ngx_memcpy(file->filename_utf8.data, file->filename.data, file->filename.len); file->filename_utf8.len = file->filename.len; file->filename_utf8.data[file->filename.len] = '\0'; //outbuf outlen = outleft = inlen * sizeof(int) + 15; file->filename.data = ngx_pnalloc(r->pool, outlen + 1); in = file->filename_utf8.data; p = file->filename.data; //reset state iconv(iconv_cd, NULL, NULL, NULL, NULL); //convert the string iconv(iconv_cd, (char **)&in, &inlen, (char **)&p, &outleft); //XXX if (res == (size_t)-1) { ? } file->filename.len = outlen - outleft; file->filename_utf8_crc32 = ngx_crc32_long(file->filename_utf8.data, file->filename_utf8.len); } #endif else if(vv->len) { const char * sep = ngx_http_zip_strnrstr((const char*)file->filename.data, file->filename.len, (const char*)vv->data, vv->len); if(sep) { size_t utf8_len = file->filename.len - vv->len - (size_t)(sep - (const char *)file->filename.data); file->filename_utf8.data = ngx_pnalloc(r->pool, utf8_len); file->filename_utf8.len = utf8_len; ngx_memcpy(file->filename_utf8.data, sep + vv->len, utf8_len); file->filename.len -= utf8_len + vv->len; file->filename_utf8_crc32 = ngx_crc32_long(file->filename_utf8.data, file->filename_utf8.len); } /* else { } */ // Separator not found. Okay, no extra field for this one then. } } if(offset >= (off_t) NGX_MAX_UINT32_VALUE) ctx->zip64_used = file->need_zip64_offset = 1; if(file->size >= (off_t) NGX_MAX_UINT32_VALUE) ctx->zip64_used = file->need_zip64 = 1; ctx->cd_size += sizeof(ngx_zip_central_directory_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_central_t) + (file->need_zip64_offset ? (file->need_zip64 ? sizeof(ngx_zip_extra_field_zip64_sizes_offset_t) : sizeof(ngx_zip_extra_field_zip64_offset_only_t)) : (file->need_zip64 ? sizeof(ngx_zip_extra_field_zip64_sizes_only_t) : 0) + (ctx->unicode_path && file->filename_utf8.len ? (sizeof(ngx_zip_extra_field_unicode_path_t) + file->filename_utf8.len): 0) ); header_piece = &ctx->pieces[piece_i++]; header_piece->type = zip_header_piece; header_piece->file = file; header_piece->range.start = offset; header_piece->range.end = offset += sizeof(ngx_zip_local_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_local_t) + (file->need_zip64? sizeof(ngx_zip_extra_field_zip64_sizes_only_t):0) + (ctx->unicode_path && file->filename_utf8.len ? (sizeof(ngx_zip_extra_field_unicode_path_t) + file->filename_utf8.len): 0); file_piece = &ctx->pieces[piece_i++]; file_piece->type = zip_file_piece; file_piece->file = file; file_piece->range.start = offset; file_piece->range.end = offset += file->size; //!note: (sizeless chunks): we need file size here / or mark it and modify ranges after if (file->missing_crc32) { // if incomplete header -> add footer with that info to file trailer_piece = &ctx->pieces[piece_i++]; trailer_piece->type = zip_trailer_piece; trailer_piece->file = file; trailer_piece->range.start = offset; trailer_piece->range.end = offset += file->need_zip64? sizeof(ngx_zip_data_descriptor_zip64_t) : sizeof(ngx_zip_data_descriptor_t); //!!TODO: if we want Ranges support - here we know it is impossible for this set //? check conf/some state and abort? } } #ifdef NGX_ZIP_HAVE_ICONV if (iconv_cd) { iconv_close(iconv_cd); } #endif ctx->zip64_used |= offset >= (off_t) NGX_MAX_UINT32_VALUE || ctx->files.nelts >= NGX_MAX_UINT16_VALUE; ctx->cd_size += sizeof(ngx_zip_end_of_central_directory_record_t); if (ctx->zip64_used) ctx->cd_size += sizeof(ngx_zip_zip64_end_of_central_directory_record_t) + sizeof(ngx_zip_zip64_end_of_central_directory_locator_t); cd_piece = &ctx->pieces[piece_i++]; cd_piece->type = zip_central_directory_piece; cd_piece->range.start = offset; cd_piece->range.end = offset += ctx->cd_size; ctx->pieces_n = piece_i; //!! nasty hack (truncating allocated array without reallocation) ctx->archive_size = offset; return NGX_OK; }
static ngx_int_t ngx_http_kafka_lua_push(lua_State *L) { ngx_http_request_t *r; ngx_http_kafka_main_conf_t *kmcf; ngx_http_kafka_ctx_t *ctx; ngx_kfk_toppar_t *toppar; ngx_kfk_buf_t *buf; ngx_str_t msg; ngx_int_t rc; size_t size, len; int32_t *crc; u_char *p, *q; const char *err; int i, n, type; n = lua_gettop(L); size = 0; for (i = 3; i <= n; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: lua_tolstring(L, i, &len); size += len; break; case LUA_TNIL: size += sizeof("nil") - 1; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { size += sizeof("true") - 1; } else { size += sizeof("false") - 1; } break; case LUA_TLIGHTUSERDATA: if (lua_touserdata(L, i) == NULL) { size += sizeof("null") - 1; break; } continue; default: err = lua_pushfstring(L, "string, number, boolean, or nil " "expected, got %s", lua_typename(L, type)); return luaL_argerror(L, i, err); } } r = ngx_http_lua_get_request(L); kmcf = ngx_http_get_module_main_conf(r, ngx_http_kafka_module); if (size > kmcf->msg_max_size) { lua_pushnil(L); lua_pushfstring(L, "message is too long, at most %d bytes", (int)(kmcf->msg_max_size)); return 2; } ctx = ngx_http_get_module_ctx(r, ngx_http_kafka_module); toppar = ctx->toppar; buf = toppar->free; if (buf == NULL) { buf = ngx_http_kafka_get_buf(ngx_kfk, &ngx_kfk->free); if (buf == NULL) { lua_pushnil(L); lua_pushliteral(L, "no bufs"); return 2; } if (buf->no_pool) { if (ngx_http_kafka_init_buf_pool(ngx_kfk, buf) != NGX_OK) { lua_pushnil(L); lua_pushliteral(L, "no enough memory"); return 2; } } toppar->free = buf; } p = ngx_pnalloc(buf->pool, NGX_KFK_MSG_HEADER_SIZE + size); if (p == NULL) { lua_pushnil(L); lua_pushliteral(L, "no enough memory"); return 2; } msg.data = p; msg.len = NGX_KFK_MSG_HEADER_SIZE + size; /* offset */ *(int64_t *)p = 0; p += sizeof(int64_t); /* msg size */ *(int32_t *)p = (int32_t)htonl(14 + len); p += sizeof(int32_t); /* crc(later calculate) */ crc = (int32_t *)p; p += sizeof(int32_t); /* magic */ *(int8_t *)p = 0; p += sizeof(int8_t); /* attr */ *(int8_t *)p = 0; p += sizeof(int8_t); /* key len */ *(int32_t *)p = (int32_t)htonl(-1); p += sizeof(int32_t); /* value len */ *(int32_t *)p = (int32_t)htonl(len); p += sizeof(int32_t); for (i = 3; i <= n; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: q = (u_char *) lua_tolstring(L, i, &len); p = ngx_copy(p, q, len); break; case LUA_TNIL: *p++ = 'n'; *p++ = 'i'; *p++ = 'l'; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { *p++ = 't'; *p++ = 'r'; *p++ = 'u'; *p++ = 'e'; } else { *p++ = 'f'; *p++ = 'a'; *p++ = 'l'; *p++ = 's'; *p++ = 'e'; } break; case LUA_TLIGHTUSERDATA: *p++ = 'n'; *p++ = 'u'; *p++ = 'l'; *p++ = 'l'; break; default: ngx_http_kafka_recycle_msg(buf->pool, &msg); return luaL_error(L, "impossible to reach here"); } } if (p - msg.data > (off_t)(NGX_KFK_MSG_HEADER_SIZE + size)) { ngx_http_kafka_recycle_msg(buf->pool, &msg); return luaL_error(L, "buffer error: %d > %d", (int) (p - msg.data), (int) size); } *crc = (int32_t)htonl(ngx_crc32_long((u_char*)(crc + 1), 10 + size)); if (buf->cnt == -1) { if (ngx_http_kafka_init_chain(buf, ctx) != NGX_OK) { ngx_http_kafka_recycle_msg(buf->pool, &msg); lua_pushnil(L); lua_pushliteral(L, "no enough memory"); return 2; } } ctx->msg = &msg; rc = ngx_http_kafka_enq_buf(r, ctx); if (rc != NGX_OK) { lua_pushnil(L); lua_pushliteral(L, "internal error"); return 2; } lua_pushinteger(L, 1); return 1; }
// make our proposed ZIP-file chunk map ngx_int_t ngx_http_zip_generate_pieces(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx) { ngx_uint_t i, piece_i; off_t offset = 0; ngx_http_zip_file_t *file; ngx_http_zip_piece_t *header_piece, *file_piece, *trailer_piece, *cd_piece; ngx_http_variable_value_t *vv; if ((vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t))) == NULL) return NGX_ERROR; ctx->unicode_path = 0; #ifdef NGX_ZIP_HAVE_ICONV iconv_t *iconv_cd = NULL; if (ngx_http_upstream_header_variable(r, vv, (uintptr_t)(&ngx_http_zip_header_charset_name)) == NGX_OK && !vv->not_found && ngx_strncmp(vv->data, "utf8", sizeof("utf8") - 1) != 0) { char encoding[ICONV_CSNMAXLEN]; snprintf(encoding, sizeof(encoding), "%s//TRANSLIT//IGNORE", vv->data); iconv_cd = iconv_open((const char *)encoding, "utf-8"); if (iconv_cd == (iconv_t)(-1)) { ngx_log_error(NGX_LOG_WARN, r->connection->log, errno, "mod_zip: iconv_open('%s', 'utf-8') failed", vv->data); iconv_cd = NULL; } } if (iconv_cd) { ctx->unicode_path = 1; } #endif // pieces: for each file: header, data, footer (if needed) -> 2 or 3 per file // plus file footer (CD + [zip64 end + zip64 locator +] end of cd) in one chunk ctx->pieces_n = ctx->files.nelts * (2 + (!!ctx->missing_crc32)) + 1; if ((ctx->pieces = ngx_palloc(r->pool, sizeof(ngx_http_zip_piece_t) * ctx->pieces_n)) == NULL) return NGX_ERROR; ctx->cd_size = 0; for (piece_i = i = 0; i < ctx->files.nelts; i++) { file = &((ngx_http_zip_file_t *)ctx->files.elts)[i]; file->offset = offset; #ifdef NGX_ZIP_HAVE_ICONV if (ctx->unicode_path) { size_t inlen = file->filename.len, outlen, outleft; u_char *p, *in; //inbuf file->filename_utf8.data = ngx_pnalloc(r->pool, file->filename.len + 1); ngx_memcpy(file->filename_utf8.data, file->filename.data, file->filename.len); file->filename_utf8.len = file->filename.len; file->filename_utf8.data[file->filename.len] = '\0'; //outbuf outlen = outleft = inlen * sizeof(int) + 15; file->filename.data = ngx_pnalloc(r->pool, outlen + 1); in = file->filename_utf8.data; p = file->filename.data; //reset state iconv(iconv_cd, NULL, NULL, NULL, NULL); //convert the string iconv(iconv_cd, (char **)&in, &inlen, (char **)&p, &outleft); //XXX if (res == (size_t)-1) { ? } file->filename.len = outlen - outleft; file->filename_utf8_crc32 = ngx_crc32_long(file->filename_utf8.data, file->filename_utf8.len); } #endif if(offset >= (off_t) NGX_MAX_UINT32_VALUE) ctx->zip64_used = file->need_zip64_offset = 1; if(file->size >= (off_t) NGX_MAX_UINT32_VALUE) ctx->zip64_used = file->need_zip64 = 1; ctx->cd_size += sizeof(ngx_zip_central_directory_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_central_t) + (file->need_zip64_offset ? (file->need_zip64 ? sizeof(ngx_zip_extra_field_zip64_sizes_offset_t) : sizeof(ngx_zip_extra_field_zip64_offset_only_t)) : (file->need_zip64 ? sizeof(ngx_zip_extra_field_zip64_sizes_only_t) : 0) + (ctx->unicode_path ? (sizeof(ngx_zip_extra_field_unicode_path_t) + file->filename_utf8.len): 0) ); header_piece = &ctx->pieces[piece_i++]; header_piece->type = zip_header_piece; header_piece->file = file; header_piece->range.start = offset; header_piece->range.end = offset += sizeof(ngx_zip_local_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_local_t) + (file->need_zip64? sizeof(ngx_zip_extra_field_zip64_sizes_only_t):0) + (ctx->unicode_path ? (sizeof(ngx_zip_extra_field_unicode_path_t) + file->filename_utf8.len): 0); file_piece = &ctx->pieces[piece_i++]; file_piece->type = zip_file_piece; file_piece->file = file; file_piece->range.start = offset; file_piece->range.end = offset += file->size; //!note: (sizeless chunks): we need file size here / or mark it and modify ranges after if (file->missing_crc32) { // if incomplete header -> add footer with that info to file trailer_piece = &ctx->pieces[piece_i++]; trailer_piece->type = zip_trailer_piece; trailer_piece->file = file; trailer_piece->range.start = offset; trailer_piece->range.end = offset += file->need_zip64? sizeof(ngx_zip_data_descriptor_zip64_t) : sizeof(ngx_zip_data_descriptor_t); //!!TODO: if we want Ranges support - here we know it is impossible for this set //? check conf/some state and abort? } } #ifdef NGX_ZIP_HAVE_ICONV if (ctx->unicode_path) { iconv_close(iconv_cd); } #endif ctx->zip64_used |= offset >= (off_t) NGX_MAX_UINT32_VALUE || ctx->files.nelts >= NGX_MAX_UINT16_VALUE; ctx->cd_size += sizeof(ngx_zip_end_of_central_directory_record_t); if (ctx->zip64_used) ctx->cd_size += sizeof(ngx_zip_zip64_end_of_central_directory_record_t) + sizeof(ngx_zip_zip64_end_of_central_directory_locator_t); cd_piece = &ctx->pieces[piece_i++]; cd_piece->type = zip_central_directory_piece; cd_piece->range.start = offset; cd_piece->range.end = offset += ctx->cd_size; ctx->pieces_n = piece_i; //!! nasty hack (truncating allocated array without reallocation) ctx->archive_size = offset; return NGX_OK; }
static ngx_int_t ngx_http_kafka_script_run(ngx_http_request_t *r, ngx_pool_t *pool, ngx_str_t *msg, void *code_lengths, size_t len, void *code_values) { ngx_http_kafka_main_conf_t *kmcf; ngx_http_script_code_pt code; ngx_http_script_len_code_pt lcode; ngx_http_script_engine_t e; ngx_http_core_main_conf_t *cmcf; ngx_uint_t i; u_char *p; int32_t *crc; cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); for (i = 0; i < cmcf->variables.nelts; i++) { if (r->variables[i].no_cacheable) { r->variables[i].valid = 0; r->variables[i].not_found = 0; } } ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); e.ip = code_lengths; e.request = r; e.flushed = 1; while (*(uintptr_t *) e.ip) { lcode = *(ngx_http_script_len_code_pt *) e.ip; len += lcode(&e); } if (len == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "[kafka] message is null, we ignore it"); return NGX_DECLINED; } kmcf = ngx_http_get_module_main_conf(r, ngx_http_kafka_module); if (len > kmcf->msg_max_size) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "[kafka] message is too large, we ignore it"); return NGX_DECLINED; } p = ngx_pnalloc(pool, NGX_KFK_MSG_HEADER_SIZE + len); if (p == NULL) { return NGX_BUSY; } msg->data = p; msg->len = NGX_KFK_MSG_HEADER_SIZE + len; /* offset */ *(int64_t *)p = 0; p += sizeof(int64_t); /* msg size */ *(int32_t *)p = (int32_t)htonl(14 + len); p += sizeof(int32_t); /* crc(later calculate) */ crc = (int32_t *)p; p += sizeof(int32_t); /* magic */ *(int8_t *)p = 0; p += sizeof(int8_t); /* attr */ *(int8_t *)p = 0; p += sizeof(int8_t); /* key len */ *(int32_t *)p = (int32_t)htonl(-1); p += sizeof(int32_t); /* value len */ *(int32_t *)p = (int32_t)htonl(len); p += sizeof(int32_t); e.ip = code_values; e.pos = p; while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); } if (e.pos == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "[kafka] some unexpected things happened when get request body"); ngx_http_kafka_recycle_msg(pool, msg); return NGX_ERROR; } *crc = (int32_t)htonl(ngx_crc32_long((u_char*)(crc + 1), 10 + len)); return NGX_OK; }