Mercurial > hg > nginx-vendor-0-6
diff src/http/modules/ngx_http_index_module.c @ 50:72eb30262aac NGINX_0_1_25
nginx 0.1.25
*) Bugfix: nginx did run on Linux parisc.
*) Feature: nginx now does not start under FreeBSD if the sysctl
kern.ipc.somaxconn value is too big.
*) Bugfix: if a request was internally redirected by the
ngx_http_index_module module to the ngx_http_proxy_module or
ngx_http_fastcgi_module modules, then the index file was not closed
after request completion.
*) Feature: the "proxy_pass" can be used in location with regular
expression.
*) Feature: the ngx_http_rewrite_filter_module module supports the
condition like "if ($HTTP_USER_AGENT ~ MSIE)".
*) Bugfix: nginx started too slow if the large number of addresses and
text values were used in the "geo" directive.
*) Change: a variable name must be declared as "$name" in the "geo"
directive. The previous variant without "$" is still supported, but
will be removed soon.
*) Feature: the "%{VARIABLE}v" logging parameter.
*) Feature: the "set $name value" directive.
*) Bugfix: gcc 4.0 compatibility.
*) Feature: the --with-openssl-opt=OPTIONS autoconfiguration directive.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Sat, 19 Mar 2005 00:00:00 +0300 |
parents | |
children | b55cbf18157e |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_index_module.c @@ -0,0 +1,558 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> + + +typedef struct { + ngx_array_t indices; + size_t max_index_len; + ngx_http_cache_hash_t *index_cache; +} ngx_http_index_loc_conf_t; + + +typedef struct { + ngx_uint_t index; + u_char *last; + ngx_str_t path; + ngx_str_t redirect; + ngx_http_cache_entry_t *cache; + ngx_uint_t tested; /* unsigned tested:1 */ +} ngx_http_index_ctx_t; + + +#define NGX_HTTP_DEFAULT_INDEX "index.html" + + +static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r, + ngx_http_index_ctx_t *ctx); +static ngx_int_t ngx_http_index_error(ngx_http_request_t *r, + ngx_http_index_ctx_t *ctx, ngx_err_t err); + +static ngx_int_t ngx_http_index_init(ngx_cycle_t *cycle); +static void *ngx_http_index_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_index_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child); +static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_command_t ngx_http_index_commands[] = { + + { ngx_string("index"), + NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_http_index_set_index, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + +#if (NGX_HTTP_CACHE) + + { ngx_string("index_cache"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE3, + ngx_http_set_cache_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_index_loc_conf_t, index_cache), + NULL }, + +#endif + + ngx_null_command +}; + + +ngx_http_module_t ngx_http_index_module_ctx = { + NULL, /* pre conf */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_index_create_loc_conf, /* create location configration */ + ngx_http_index_merge_loc_conf /* merge location configration */ +}; + + +ngx_module_t ngx_http_index_module = { + NGX_MODULE, + &ngx_http_index_module_ctx, /* module context */ + ngx_http_index_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_index_init, /* init module */ + NULL /* init process */ +}; + + +/* + * Try to open the first index file before the test of the directory existence + * because the valid requests should be many more than invalid ones. + * If open() would fail, then stat() should be more quickly because some data + * is already cached in the kernel. + * Besides, Win32 has ERROR_PATH_NOT_FOUND (NGX_ENOTDIR). + * Unix has ENOTDIR error, although it less helpfull - it points only + * that path contains the usual file in place of the directory. + */ + +static ngx_int_t ngx_http_index_handler(ngx_http_request_t *r) +{ + u_char *name; + ngx_fd_t fd; + ngx_int_t rc; + ngx_str_t *index; + ngx_err_t err; + ngx_log_t *log; + ngx_http_index_ctx_t *ctx; + ngx_http_core_loc_conf_t *clcf; + ngx_http_index_loc_conf_t *ilcf; +#if (NGX_HTTP_CACHE0) + /* crc must be in ctx !! */ + uint32_t crc; +#endif + + if (r->uri.data[r->uri.len - 1] != '/') { + return NGX_DECLINED; + } + + /* TODO: Win32 */ + if (r->zero_in_uri) { + return NGX_DECLINED; + } + + log = r->connection->log; + + /* + * we use context because the handler supports an async file opening + * and thus can be called several times + */ + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); + + ctx = ngx_http_get_module_ctx(r, ngx_http_index_module); + if (ctx == NULL) { + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_index_ctx_t)); + if (ctx == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_http_set_ctx(r, ctx, ngx_http_index_module); + +#if (NGX_HTTP_CACHE) + + if (ilcf->index_cache) { + ctx->cache = ngx_http_cache_get(ilcf->index_cache, NULL, + &r->uri, &crc); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "http index cache get: %p", ctx->cache); + + if (ctx->cache && !ctx->cache->expired) { + + ctx->cache->accessed = ngx_cached_time; + + ctx->redirect.len = ctx->cache->data.value.len; + ctx->redirect.data = ngx_palloc(r->pool, ctx->redirect.len + 1); + if (ctx->redirect.data == NULL) { + ngx_http_cache_unlock(ilcf->index_cache, ctx->cache, log); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_memcpy(ctx->redirect.data, ctx->cache->data.value.data, + ctx->redirect.len + 1); + ngx_http_cache_unlock(ilcf->index_cache, ctx->cache, log); + + return ngx_http_internal_redirect(r, &ctx->redirect, NULL); + } + } + +#endif + +#if 0 + ctx->path.data = ngx_palloc(r->pool, clcf->root.len + r->uri.len + + ilcf->max_index_len + - clcf->alias * clcf->name.len); + if (ctx->path.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx->redirect.data = ngx_cpymem(ctx->path.data, clcf->root.data, + clcf->root.len); +#endif + + if (clcf->alias) { + ctx->path.data = ngx_palloc(r->pool, clcf->root.len + + r->uri.len + 1 - clcf->name.len + + ilcf->max_index_len); + if (ctx->path.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx->redirect.data = ngx_palloc(r->pool, r->uri.len + + ilcf->max_index_len); + if (ctx->redirect.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_memcpy(ctx->path.data, clcf->root.data, clcf->root.len); + + ctx->last = ngx_cpystrn(ctx->path.data + clcf->root.len, + r->uri.data + clcf->name.len, + r->uri.len + 1 - clcf->name.len); + +#if 0 + /* + * aliases usually have trailling "/", + * set it in the start of the possible redirect + */ + + if (*ctx->redirect.data != '/') { + ctx->redirect.data--; + } +#endif + + } else { + ctx->path.data = ngx_palloc(r->pool, clcf->root.len + r->uri.len + + ilcf->max_index_len); + if (ctx->path.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx->redirect.data = ngx_cpymem(ctx->path.data, clcf->root.data, + clcf->root.len); + + ctx->last = ngx_cpystrn(ctx->redirect.data, r->uri.data, + r->uri.len + 1); + } + } + + ctx->path.len = ctx->last - ctx->path.data; + + index = ilcf->indices.elts; + for (/* void */; ctx->index < ilcf->indices.nelts; ctx->index++) { + + if (index[ctx->index].data[0] == '/') { + name = index[ctx->index].data; + + } else { + ngx_memcpy(ctx->last, index[ctx->index].data, + index[ctx->index].len + 1); + name = ctx->path.data; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "open index \"%s\"", name); + + fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN); + + if (fd == (ngx_fd_t) NGX_AGAIN) { + return NGX_AGAIN; + } + + if (fd == NGX_INVALID_FILE) { + err = ngx_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, err, + ngx_open_file_n " \"%s\" failed", name); + + if (err == NGX_ENOTDIR) { + return ngx_http_index_error(r, ctx, err); + + } else if (err == NGX_EACCES) { + return ngx_http_index_error(r, ctx, err); + } + + if (!ctx->tested) { + rc = ngx_http_index_test_dir(r, ctx); + + if (rc != NGX_OK) { + return rc; + } + + ctx->tested = 1; + } + + if (err == NGX_ENOENT) { + continue; + } + + ngx_log_error(NGX_LOG_ERR, log, err, + ngx_open_file_n " \"%s\" failed", name); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + + /* STUB: open file cache */ + + r->file.name.data = name; + r->file.fd = fd; + + if (index[ctx->index].data[0] == '/') { + r->file.name.len = index[ctx->index].len; + ctx->redirect.len = index[ctx->index].len; + ctx->redirect.data = index[ctx->index].data; + + } else { + if (clcf->alias) { + name = ngx_cpymem(ctx->redirect.data, r->uri.data, r->uri.len); + ngx_memcpy(name, index[ctx->index].data, + index[ctx->index].len + 1); + } + + ctx->redirect.len = r->uri.len + index[ctx->index].len; + r->file.name.len = clcf->root.len + r->uri.len + - clcf->alias * clcf->name.len + + index[ctx->index].len; + } + + /**/ + + +#if (NGX_HTTP_CACHE) + + if (ilcf->index_cache) { + + if (ctx->cache) { + if (ctx->redirect.len == ctx->cache->data.value.len + && ngx_memcmp(ctx->cache->data.value.data, + ctx->redirect.data, ctx->redirect.len) == 0) + { + ctx->cache->accessed = ngx_cached_time; + ctx->cache->updated = ngx_cached_time; + ngx_http_cache_unlock(ilcf->index_cache, ctx->cache, log); + + return ngx_http_internal_redirect(r, &ctx->redirect, NULL); + } + } + + ctx->redirect.len++; + ctx->cache = ngx_http_cache_alloc(ilcf->index_cache, ctx->cache, + NULL, &r->uri, crc, + &ctx->redirect, log); + ctx->redirect.len--; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "http index cache alloc: %p", ctx->cache); + + if (ctx->cache) { + ctx->cache->fd = NGX_INVALID_FILE; + ctx->cache->accessed = ngx_cached_time; + ctx->cache->last_modified = 0; + ctx->cache->updated = ngx_cached_time; + ctx->cache->memory = 1; + ngx_http_cache_unlock(ilcf->index_cache, ctx->cache, log); + } + } + +#endif + + return ngx_http_internal_redirect(r, &ctx->redirect, NULL); + } + + return NGX_DECLINED; +} + + +static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r, + ngx_http_index_ctx_t *ctx) +{ + ngx_err_t err; + + ctx->path.data[ctx->path.len - 1] = '\0'; + ctx->path.data[ctx->path.len] = '\0'; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http check dir: \"%s\"", ctx->path.data); + + if (ngx_file_info(ctx->path.data, &r->file.info) == -1) { + + err = ngx_errno; + + if (err == NGX_ENOENT) { + ctx->path.data[ctx->path.len - 1] = '/'; + return ngx_http_index_error(r, ctx, err); + } + + ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, + ngx_file_info_n " \"%s\" failed", ctx->path.data); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx->path.data[ctx->path.len - 1] = '/'; + + if (ngx_is_dir(&r->file.info)) { + return NGX_OK; + } + + /* THINK: not reached ??? */ + return ngx_http_index_error(r, ctx, 0); +} + + +static ngx_int_t ngx_http_index_error(ngx_http_request_t *r, + ngx_http_index_ctx_t *ctx, ngx_err_t err) +{ + if (err == NGX_EACCES) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, err, + "\"%s\" is forbidden", ctx->path.data); + + return NGX_HTTP_FORBIDDEN; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, err, + "\"%s\" is not found", ctx->path.data); + return NGX_HTTP_NOT_FOUND; +} + + +static ngx_int_t ngx_http_index_init(ngx_cycle_t *cycle) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_http_index_handler; + + return NGX_OK; +} + + +static void *ngx_http_index_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_index_loc_conf_t *conf; + + conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t)); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + + if (ngx_array_init(&conf->indices, cf->pool, 2, sizeof(ngx_str_t)) + == NGX_ERROR) + { + return NGX_CONF_ERROR; + } + + conf->max_index_len = 0; + + conf->index_cache = NULL; + + return conf; +} + + +/* TODO: remove duplicate indices */ + +static char *ngx_http_index_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child) +{ + ngx_http_index_loc_conf_t *prev = parent; + ngx_http_index_loc_conf_t *conf = child; + + ngx_str_t *index; + + if (conf->max_index_len == 0) { + if (prev->max_index_len != 0) { + ngx_memcpy(conf, prev, sizeof(ngx_http_index_loc_conf_t)); + return NGX_CONF_OK; + } + + index = ngx_array_push(&conf->indices); + if (index == NULL) { + return NGX_CONF_ERROR; + } + + index->len = sizeof(NGX_HTTP_DEFAULT_INDEX) - 1; + index->data = (u_char *) NGX_HTTP_DEFAULT_INDEX; + conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX); + + return NGX_CONF_OK; + } + +#if 0 + + if (prev->max_index_len != 0) { + + prev_index = prev->indices.elts; + for (i = 0; i < prev->indices.nelts; i++) { + index = ngx_array_push(&conf->indices); + if (index == NULL) { + return NGX_CONF_ERROR; + } + + index->len = prev_index[i].len; + index->data = prev_index[i].data; + } + } + + if (conf->max_index_len < prev->max_index_len) { + conf->max_index_len = prev->max_index_len; + } + +#endif + + if (conf->index_cache == NULL) { + conf->index_cache = prev->index_cache; + } + + return NGX_CONF_OK; +} + + +/* TODO: warn about duplicate indices */ + +static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_http_index_loc_conf_t *ilcf = conf; + + ngx_uint_t i; + ngx_str_t *index, *value; + + value = cf->args->elts; + + if (value[1].data[0] == '/' && ilcf->indices.nelts == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "first index \"%V\" in \"%V\" directive " + "must not be absolute", + &value[1], &cmd->name); + return NGX_CONF_ERROR; + } + + for (i = 1; i < cf->args->nelts; i++) { + if (value[i].len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "index \"%V\" in \"%V\" directive is invalid", + &value[1], &cmd->name); + return NGX_CONF_ERROR; + } + + index = ngx_array_push(&ilcf->indices); + if (index == NULL) { + return NGX_CONF_ERROR; + } + + index->len = value[i].len; + index->data = value[i].data; + + if (ilcf->max_index_len < index->len + 1) { + ilcf->max_index_len = index->len + 1; + } + } + + return NGX_CONF_OK; +}