3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
15 ngx_http_cache_hash_t
*index_cache
;
16 } ngx_http_index_loc_conf_t
;
24 ngx_http_cache_entry_t
*cache
;
25 ngx_uint_t tested
; /* unsigned tested:1 */
26 } ngx_http_index_ctx_t
;
29 #define NGX_HTTP_DEFAULT_INDEX "index.html"
32 static ngx_int_t
ngx_http_index_test_dir(ngx_http_request_t
*r
,
33 ngx_http_index_ctx_t
*ctx
);
34 static ngx_int_t
ngx_http_index_error(ngx_http_request_t
*r
,
35 ngx_http_index_ctx_t
*ctx
, ngx_err_t err
);
37 static ngx_int_t
ngx_http_index_init(ngx_cycle_t
*cycle
);
38 static void *ngx_http_index_create_loc_conf(ngx_conf_t
*cf
);
39 static char *ngx_http_index_merge_loc_conf(ngx_conf_t
*cf
,
40 void *parent
, void *child
);
41 static char *ngx_http_index_set_index(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
45 static ngx_command_t ngx_http_index_commands
[] = {
47 { ngx_string("index"),
48 NGX_HTTP_LOC_CONF
|NGX_CONF_1MORE
,
49 ngx_http_index_set_index
,
50 NGX_HTTP_LOC_CONF_OFFSET
,
56 { ngx_string("index_cache"),
57 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_TAKE3
,
58 ngx_http_set_cache_slot
,
59 NGX_HTTP_LOC_CONF_OFFSET
,
60 offsetof(ngx_http_index_loc_conf_t
, index_cache
),
69 ngx_http_module_t ngx_http_index_module_ctx
= {
72 NULL
, /* create main configuration */
73 NULL
, /* init main configuration */
75 NULL
, /* create server configuration */
76 NULL
, /* merge server configuration */
78 ngx_http_index_create_loc_conf
, /* create location configration */
79 ngx_http_index_merge_loc_conf
/* merge location configration */
83 ngx_module_t ngx_http_index_module
= {
85 &ngx_http_index_module_ctx
, /* module context */
86 ngx_http_index_commands
, /* module directives */
87 NGX_HTTP_MODULE
, /* module type */
88 ngx_http_index_init
, /* init module */
94 * Try to open the first index file before the test of the directory existence
95 * because the valid requests should be many more than invalid ones.
96 * If open() failed then stat() should be more quickly because some data
97 * is already cached in the kernel.
98 * Besides Win32 has ERROR_PATH_NOT_FOUND (NGX_ENOTDIR).
99 * Unix has ENOTDIR error, although it less helpfull - it shows only
100 * that path contains the usual file in place of the directory.
103 static ngx_int_t
ngx_http_index_handler(ngx_http_request_t
*r
)
111 ngx_http_index_ctx_t
*ctx
;
112 ngx_http_core_loc_conf_t
*clcf
;
113 ngx_http_index_loc_conf_t
*ilcf
;
114 #if (NGX_HTTP_CACHE0)
115 /* crc must be in ctx !! */
119 if (r
->uri
.data
[r
->uri
.len
- 1] != '/') {
124 if (r
->zero_in_uri
) {
128 log
= r
->connection
->log
;
131 * we use context because the handler supports an async file opening
132 * and thus can be called several times
135 clcf
= ngx_http_get_module_loc_conf(r
, ngx_http_core_module
);
136 ilcf
= ngx_http_get_module_loc_conf(r
, ngx_http_index_module
);
138 ctx
= ngx_http_get_module_ctx(r
, ngx_http_index_module
);
140 ngx_http_create_ctx(r
, ctx
, ngx_http_index_module
,
141 sizeof(ngx_http_index_ctx_t
),
142 NGX_HTTP_INTERNAL_SERVER_ERROR
);
146 if (ilcf
->index_cache
) {
147 ctx
->cache
= ngx_http_cache_get(ilcf
->index_cache
, NULL
,
150 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, log
, 0,
151 "http index cache get: %p", ctx
->cache
);
153 if (ctx
->cache
&& !ctx
->cache
->expired
) {
155 ctx
->cache
->accessed
= ngx_cached_time
;
157 ctx
->redirect
.len
= ctx
->cache
->data
.value
.len
;
158 ctx
->redirect
.data
= ngx_palloc(r
->pool
, ctx
->redirect
.len
+ 1);
159 if (ctx
->redirect
.data
== NULL
) {
160 ngx_http_cache_unlock(ilcf
->index_cache
, ctx
->cache
, log
);
161 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
164 ngx_memcpy(ctx
->redirect
.data
, ctx
->cache
->data
.value
.data
,
165 ctx
->redirect
.len
+ 1);
166 ngx_http_cache_unlock(ilcf
->index_cache
, ctx
->cache
, log
);
168 return ngx_http_internal_redirect(r
, &ctx
->redirect
, NULL
);
175 ctx
->path
.data
= ngx_palloc(r
->pool
, clcf
->root
.len
+ r
->uri
.len
176 + ilcf
->max_index_len
177 - clcf
->alias
* clcf
->name
.len
);
178 if (ctx
->path
.data
== NULL
) {
179 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
182 ctx
->redirect
.data
= ngx_cpymem(ctx
->path
.data
, clcf
->root
.data
,
187 ctx
->path
.data
= ngx_palloc(r
->pool
, clcf
->root
.len
188 + r
->uri
.len
+ 1 - clcf
->name
.len
189 + ilcf
->max_index_len
);
190 if (ctx
->path
.data
== NULL
) {
191 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
194 ctx
->redirect
.data
= ngx_palloc(r
->pool
, r
->uri
.len
195 + ilcf
->max_index_len
);
196 if (ctx
->redirect
.data
== NULL
) {
197 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
200 ngx_memcpy(ctx
->path
.data
, clcf
->root
.data
, clcf
->root
.len
);
202 ctx
->last
= ngx_cpystrn(ctx
->path
.data
+ clcf
->root
.len
,
203 r
->uri
.data
+ clcf
->name
.len
,
204 r
->uri
.len
+ 1 - clcf
->name
.len
);
208 * aliases usually have trailling "/",
209 * set it in the start of the possible redirect
212 if (*ctx
->redirect
.data
!= '/') {
213 ctx
->redirect
.data
--;
218 ctx
->path
.data
= ngx_palloc(r
->pool
, clcf
->root
.len
+ r
->uri
.len
219 + ilcf
->max_index_len
);
220 if (ctx
->path
.data
== NULL
) {
221 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
224 ctx
->redirect
.data
= ngx_cpymem(ctx
->path
.data
, clcf
->root
.data
,
227 ctx
->last
= ngx_cpystrn(ctx
->redirect
.data
, r
->uri
.data
,
232 ctx
->path
.len
= ctx
->last
- ctx
->path
.data
;
234 index
= ilcf
->indices
.elts
;
235 for (/* void */; ctx
->index
< ilcf
->indices
.nelts
; ctx
->index
++) {
237 if (index
[ctx
->index
].data
[0] == '/') {
238 name
= index
[ctx
->index
].data
;
241 ngx_memcpy(ctx
->last
, index
[ctx
->index
].data
,
242 index
[ctx
->index
].len
+ 1);
243 name
= ctx
->path
.data
;
246 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, log
, 0,
247 "open index \"%s\"", name
);
249 fd
= ngx_open_file(name
, NGX_FILE_RDONLY
, NGX_FILE_OPEN
);
251 if (fd
== (ngx_fd_t
) NGX_AGAIN
) {
255 if (fd
== NGX_INVALID_FILE
) {
258 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, log
, err
,
259 ngx_open_file_n
" \"%s\" failed", name
);
261 if (err
== NGX_ENOTDIR
) {
262 return ngx_http_index_error(r
, ctx
, err
);
264 } else if (err
== NGX_EACCES
) {
265 return ngx_http_index_error(r
, ctx
, err
);
269 rc
= ngx_http_index_test_dir(r
, ctx
);
278 if (err
== NGX_ENOENT
) {
282 ngx_log_error(NGX_LOG_ERR
, log
, err
,
283 ngx_open_file_n
" \"%s\" failed", name
);
285 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
289 /* STUB: open file cache */
291 r
->file
.name
.data
= name
;
294 if (index
[ctx
->index
].data
[0] == '/') {
295 r
->file
.name
.len
= index
[ctx
->index
].len
;
296 ctx
->redirect
.len
= index
[ctx
->index
].len
;
297 ctx
->redirect
.data
= index
[ctx
->index
].data
;
301 name
= ngx_cpymem(ctx
->redirect
.data
, r
->uri
.data
, r
->uri
.len
);
302 ngx_memcpy(name
, index
[ctx
->index
].data
,
303 index
[ctx
->index
].len
+ 1);
306 ctx
->redirect
.len
= r
->uri
.len
+ index
[ctx
->index
].len
;
307 r
->file
.name
.len
= clcf
->root
.len
+ r
->uri
.len
308 - clcf
->alias
* clcf
->name
.len
309 + index
[ctx
->index
].len
;
317 if (ilcf
->index_cache
) {
320 if (ctx
->redirect
.len
== ctx
->cache
->data
.value
.len
321 && ngx_memcmp(ctx
->cache
->data
.value
.data
,
322 ctx
->redirect
.data
, ctx
->redirect
.len
) == 0)
324 ctx
->cache
->accessed
= ngx_cached_time
;
325 ctx
->cache
->updated
= ngx_cached_time
;
326 ngx_http_cache_unlock(ilcf
->index_cache
, ctx
->cache
, log
);
328 return ngx_http_internal_redirect(r
, &ctx
->redirect
, NULL
);
333 ctx
->cache
= ngx_http_cache_alloc(ilcf
->index_cache
, ctx
->cache
,
335 &ctx
->redirect
, log
);
338 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, log
, 0,
339 "http index cache alloc: %p", ctx
->cache
);
342 ctx
->cache
->fd
= NGX_INVALID_FILE
;
343 ctx
->cache
->accessed
= ngx_cached_time
;
344 ctx
->cache
->last_modified
= 0;
345 ctx
->cache
->updated
= ngx_cached_time
;
346 ctx
->cache
->memory
= 1;
347 ngx_http_cache_unlock(ilcf
->index_cache
, ctx
->cache
, log
);
353 return ngx_http_internal_redirect(r
, &ctx
->redirect
, NULL
);
360 static ngx_int_t
ngx_http_index_test_dir(ngx_http_request_t
*r
,
361 ngx_http_index_ctx_t
*ctx
)
365 ctx
->path
.data
[ctx
->path
.len
- 1] = '\0';
366 ctx
->path
.data
[ctx
->path
.len
] = '\0';
368 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
369 "http check dir: \"%s\"", ctx
->path
.data
);
371 if (ngx_file_info(ctx
->path
.data
, &r
->file
.info
) == -1) {
375 if (err
== NGX_ENOENT
) {
376 ctx
->path
.data
[ctx
->path
.len
- 1] = '/';
377 return ngx_http_index_error(r
, ctx
, err
);
380 ngx_log_error(NGX_LOG_CRIT
, r
->connection
->log
, err
,
381 ngx_file_info_n
" \"%s\" failed", ctx
->path
.data
);
383 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
386 ctx
->path
.data
[ctx
->path
.len
- 1] = '/';
388 if (ngx_is_dir(&r
->file
.info
)) {
392 /* THINK: not reached ??? */
393 return ngx_http_index_error(r
, ctx
, 0);
397 static ngx_int_t
ngx_http_index_error(ngx_http_request_t
*r
,
398 ngx_http_index_ctx_t
*ctx
, ngx_err_t err
)
400 if (err
== NGX_EACCES
) {
401 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, err
,
402 "\"%s\" is forbidden", ctx
->path
.data
);
404 return NGX_HTTP_FORBIDDEN
;
407 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, err
,
408 "\"%s\" is not found", ctx
->path
.data
);
409 return NGX_HTTP_NOT_FOUND
;
413 static ngx_int_t
ngx_http_index_init(ngx_cycle_t
*cycle
)
415 ngx_http_handler_pt
*h
;
416 ngx_http_core_main_conf_t
*cmcf
;
418 cmcf
= ngx_http_cycle_get_module_main_conf(cycle
, ngx_http_core_module
);
420 h
= ngx_push_array(&cmcf
->phases
[NGX_HTTP_CONTENT_PHASE
].handlers
);
425 *h
= ngx_http_index_handler
;
431 static void *ngx_http_index_create_loc_conf(ngx_conf_t
*cf
)
433 ngx_http_index_loc_conf_t
*conf
;
435 ngx_test_null(conf
, ngx_palloc(cf
->pool
, sizeof(ngx_http_index_loc_conf_t
)),
438 ngx_init_array(conf
->indices
, cf
->pool
, 3, sizeof(ngx_str_t
),
440 conf
->max_index_len
= 0;
442 conf
->index_cache
= NULL
;
448 /* TODO: remove duplicate indices */
450 static char *ngx_http_index_merge_loc_conf(ngx_conf_t
*cf
,
451 void *parent
, void *child
)
453 ngx_http_index_loc_conf_t
*prev
= parent
;
454 ngx_http_index_loc_conf_t
*conf
= child
;
458 if (conf
->max_index_len
== 0) {
459 if (prev
->max_index_len
!= 0) {
460 ngx_memcpy(conf
, prev
, sizeof(ngx_http_index_loc_conf_t
));
464 ngx_test_null(index
, ngx_push_array(&conf
->indices
), NGX_CONF_ERROR
);
465 index
->len
= sizeof(NGX_HTTP_DEFAULT_INDEX
) - 1;
466 index
->data
= (u_char
*) NGX_HTTP_DEFAULT_INDEX
;
467 conf
->max_index_len
= sizeof(NGX_HTTP_DEFAULT_INDEX
);
474 if (prev
->max_index_len
!= 0) {
476 prev_index
= prev
->indices
.elts
;
477 for (i
= 0; i
< prev
->indices
.nelts
; i
++) {
478 ngx_test_null(index
, ngx_push_array(&conf
->indices
),
480 index
->len
= prev_index
[i
].len
;
481 index
->data
= prev_index
[i
].data
;
485 if (conf
->max_index_len
< prev
->max_index_len
) {
486 conf
->max_index_len
= prev
->max_index_len
;
491 if (conf
->index_cache
== NULL
) {
492 conf
->index_cache
= prev
->index_cache
;
499 /* TODO: warn about duplicate indices */
501 static char *ngx_http_index_set_index(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
504 ngx_http_index_loc_conf_t
*ilcf
= conf
;
507 ngx_str_t
*index
, *value
;
509 value
= cf
->args
->elts
;
511 if (value
[1].data
[0] == '/' && ilcf
->indices
.nelts
== 0) {
512 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
513 "first index \"%V\" in \"%V\" directive "
514 "must not be absolute",
515 &value
[1], &cmd
->name
);
516 return NGX_CONF_ERROR
;
519 for (i
= 1; i
< cf
->args
->nelts
; i
++) {
520 if (value
[i
].len
== 0) {
521 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
522 "index \"%V\" in \"%V\" directive is invalid",
523 &value
[1], &cmd
->name
);
524 return NGX_CONF_ERROR
;
527 ngx_test_null(index
, ngx_push_array(&ilcf
->indices
), NGX_CONF_ERROR
);
528 index
->len
= value
[i
].len
;
529 index
->data
= value
[i
].data
;
531 if (ilcf
->max_index_len
< index
->len
+ 1) {
532 ilcf
->max_index_len
= index
->len
+ 1;