3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
21 ngx_array_t
*indices
; /* array of ngx_http_index_t */
23 } ngx_http_index_loc_conf_t
;
26 #define NGX_HTTP_DEFAULT_INDEX "index.html"
29 static ngx_int_t
ngx_http_index_test_dir(ngx_http_request_t
*r
,
30 ngx_http_core_loc_conf_t
*clcf
, u_char
*path
, u_char
*last
);
31 static ngx_int_t
ngx_http_index_error(ngx_http_request_t
*r
,
32 ngx_http_core_loc_conf_t
*clcf
, u_char
*file
, ngx_err_t err
);
34 static ngx_int_t
ngx_http_index_init(ngx_conf_t
*cf
);
35 static void *ngx_http_index_create_loc_conf(ngx_conf_t
*cf
);
36 static char *ngx_http_index_merge_loc_conf(ngx_conf_t
*cf
,
37 void *parent
, void *child
);
38 static char *ngx_http_index_set_index(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
42 static ngx_command_t ngx_http_index_commands
[] = {
44 { ngx_string("index"),
45 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_1MORE
,
46 ngx_http_index_set_index
,
47 NGX_HTTP_LOC_CONF_OFFSET
,
55 static ngx_http_module_t ngx_http_index_module_ctx
= {
56 NULL
, /* preconfiguration */
57 ngx_http_index_init
, /* postconfiguration */
59 NULL
, /* create main configuration */
60 NULL
, /* init main configuration */
62 NULL
, /* create server configuration */
63 NULL
, /* merge server configuration */
65 ngx_http_index_create_loc_conf
, /* create location configuration */
66 ngx_http_index_merge_loc_conf
/* merge location configuration */
70 ngx_module_t ngx_http_index_module
= {
72 &ngx_http_index_module_ctx
, /* module context */
73 ngx_http_index_commands
, /* module directives */
74 NGX_HTTP_MODULE
, /* module type */
75 NULL
, /* init master */
76 NULL
, /* init module */
77 NULL
, /* init process */
78 NULL
, /* init thread */
79 NULL
, /* exit thread */
80 NULL
, /* exit process */
81 NULL
, /* exit master */
87 * Try to open/test the first index file before the test of directory
88 * existence because valid requests should prevail over invalid ones.
89 * If open()/stat() of a file will fail then stat() of a directory
90 * should be faster because kernel may have already cached some data.
91 * Besides, Win32 may return ERROR_PATH_NOT_FOUND (NGX_ENOTDIR) at once.
92 * Unix has ENOTDIR error; however, it's less helpful than Win32's one:
93 * it only indicates that path points to a regular file, not a directory.
97 ngx_http_index_handler(ngx_http_request_t
*r
)
100 size_t len
, root
, reserve
, allocated
;
103 ngx_uint_t i
, dir_tested
;
104 ngx_http_index_t
*index
;
105 ngx_open_file_info_t of
;
106 ngx_http_script_code_pt code
;
107 ngx_http_script_engine_t e
;
108 ngx_http_core_loc_conf_t
*clcf
;
109 ngx_http_index_loc_conf_t
*ilcf
;
110 ngx_http_script_len_code_pt lcode
;
112 if (r
->uri
.data
[r
->uri
.len
- 1] != '/') {
116 if (!(r
->method
& (NGX_HTTP_GET
|NGX_HTTP_HEAD
|NGX_HTTP_POST
))) {
120 ilcf
= ngx_http_get_module_loc_conf(r
, ngx_http_index_module
);
121 clcf
= ngx_http_get_module_loc_conf(r
, ngx_http_core_module
);
127 /* suppress MSVC warning */
130 index
= ilcf
->indices
->elts
;
131 for (i
= 0; i
< ilcf
->indices
->nelts
; i
++) {
133 if (index
[i
].lengths
== NULL
) {
135 if (index
[i
].name
.data
[0] == '/') {
136 return ngx_http_internal_redirect(r
, &index
[i
].name
, &r
->args
);
139 reserve
= ilcf
->max_index_len
;
140 len
= index
[i
].name
.len
;
143 ngx_memzero(&e
, sizeof(ngx_http_script_engine_t
));
145 e
.ip
= index
[i
].lengths
->elts
;
149 /* 1 is for terminating '\0' as in static names */
152 while (*(uintptr_t *) e
.ip
) {
153 lcode
= *(ngx_http_script_len_code_pt
*) e
.ip
;
157 /* 16 bytes are preallocation */
162 if (reserve
> allocated
) {
164 name
= ngx_http_map_uri_to_path(r
, &path
, &root
, reserve
);
169 allocated
= path
.data
+ path
.len
- name
;
172 if (index
[i
].values
== NULL
) {
174 /* index[i].name.len includes the terminating '\0' */
176 ngx_memcpy(name
, index
[i
].name
.data
, index
[i
].name
.len
);
178 path
.len
= (name
+ index
[i
].name
.len
- 1) - path
.data
;
181 e
.ip
= index
[i
].values
->elts
;
184 while (*(uintptr_t *) e
.ip
) {
185 code
= *(ngx_http_script_code_pt
*) e
.ip
;
186 code((ngx_http_script_engine_t
*) &e
);
192 return ngx_http_internal_redirect(r
, &uri
, &r
->args
);
195 path
.len
= e
.pos
- path
.data
;
200 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
201 "open index \"%V\"", &path
);
203 ngx_memzero(&of
, sizeof(ngx_open_file_info_t
));
205 of
.read_ahead
= clcf
->read_ahead
;
206 of
.directio
= clcf
->directio
;
207 of
.valid
= clcf
->open_file_cache_valid
;
208 of
.min_uses
= clcf
->open_file_cache_min_uses
;
210 of
.errors
= clcf
->open_file_cache_errors
;
211 of
.events
= clcf
->open_file_cache_events
;
213 if (ngx_http_set_disable_symlinks(r
, clcf
, &path
, &of
) != NGX_OK
) {
214 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
217 if (ngx_open_cached_file(clcf
->open_file_cache
, &path
, &of
, r
->pool
)
220 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, of
.err
,
221 "%s \"%s\" failed", of
.failed
, path
.data
);
224 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
227 #if (NGX_HAVE_OPENAT)
228 if (of
.err
== NGX_EMLINK
229 || of
.err
== NGX_ELOOP
)
231 return NGX_HTTP_FORBIDDEN
;
235 if (of
.err
== NGX_ENOTDIR
236 || of
.err
== NGX_ENAMETOOLONG
237 || of
.err
== NGX_EACCES
)
239 return ngx_http_index_error(r
, clcf
, path
.data
, of
.err
);
243 rc
= ngx_http_index_test_dir(r
, clcf
, path
.data
, name
- 1);
252 if (of
.err
== NGX_ENOENT
) {
256 ngx_log_error(NGX_LOG_CRIT
, r
->connection
->log
, of
.err
,
257 "%s \"%s\" failed", of
.failed
, path
.data
);
259 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
262 uri
.len
= r
->uri
.len
+ len
- 1;
265 uri
.data
= path
.data
+ root
;
268 uri
.data
= ngx_pnalloc(r
->pool
, uri
.len
);
269 if (uri
.data
== NULL
) {
270 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
273 p
= ngx_copy(uri
.data
, r
->uri
.data
, r
->uri
.len
);
274 ngx_memcpy(p
, name
, len
- 1);
277 return ngx_http_internal_redirect(r
, &uri
, &r
->args
);
285 ngx_http_index_test_dir(ngx_http_request_t
*r
, ngx_http_core_loc_conf_t
*clcf
,
286 u_char
*path
, u_char
*last
)
290 ngx_open_file_info_t of
;
293 if (c
!= '/' || path
== last
) {
294 /* "alias" without trailing slash */
299 dir
.len
= last
- path
;
302 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
303 "http index check dir: \"%V\"", &dir
);
305 ngx_memzero(&of
, sizeof(ngx_open_file_info_t
));
309 of
.valid
= clcf
->open_file_cache_valid
;
310 of
.errors
= clcf
->open_file_cache_errors
;
312 if (ngx_http_set_disable_symlinks(r
, clcf
, &dir
, &of
) != NGX_OK
) {
313 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
316 if (ngx_open_cached_file(clcf
->open_file_cache
, &dir
, &of
, r
->pool
)
321 #if (NGX_HAVE_OPENAT)
322 if (of
.err
== NGX_EMLINK
323 || of
.err
== NGX_ELOOP
)
325 return NGX_HTTP_FORBIDDEN
;
329 if (of
.err
== NGX_ENOENT
) {
331 return ngx_http_index_error(r
, clcf
, dir
.data
, NGX_ENOENT
);
334 if (of
.err
== NGX_EACCES
) {
339 * ngx_http_index_test_dir() is called after the first index
340 * file testing has returned an error distinct from NGX_EACCES.
341 * This means that directory searching is allowed.
347 ngx_log_error(NGX_LOG_CRIT
, r
->connection
->log
, of
.err
,
348 "%s \"%s\" failed", of
.failed
, dir
.data
);
351 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
360 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, 0,
361 "\"%s\" is not a directory", dir
.data
);
363 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
368 ngx_http_index_error(ngx_http_request_t
*r
, ngx_http_core_loc_conf_t
*clcf
,
369 u_char
*file
, ngx_err_t err
)
371 if (err
== NGX_EACCES
) {
372 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, err
,
373 "\"%s\" is forbidden", file
);
375 return NGX_HTTP_FORBIDDEN
;
378 if (clcf
->log_not_found
) {
379 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, err
,
380 "\"%s\" is not found", file
);
383 return NGX_HTTP_NOT_FOUND
;
388 ngx_http_index_create_loc_conf(ngx_conf_t
*cf
)
390 ngx_http_index_loc_conf_t
*conf
;
392 conf
= ngx_palloc(cf
->pool
, sizeof(ngx_http_index_loc_conf_t
));
397 conf
->indices
= NULL
;
398 conf
->max_index_len
= 0;
405 ngx_http_index_merge_loc_conf(ngx_conf_t
*cf
, void *parent
, void *child
)
407 ngx_http_index_loc_conf_t
*prev
= parent
;
408 ngx_http_index_loc_conf_t
*conf
= child
;
410 ngx_http_index_t
*index
;
412 if (conf
->indices
== NULL
) {
413 conf
->indices
= prev
->indices
;
414 conf
->max_index_len
= prev
->max_index_len
;
417 if (conf
->indices
== NULL
) {
418 conf
->indices
= ngx_array_create(cf
->pool
, 1, sizeof(ngx_http_index_t
));
419 if (conf
->indices
== NULL
) {
420 return NGX_CONF_ERROR
;
423 index
= ngx_array_push(conf
->indices
);
425 return NGX_CONF_ERROR
;
428 index
->name
.len
= sizeof(NGX_HTTP_DEFAULT_INDEX
);
429 index
->name
.data
= (u_char
*) NGX_HTTP_DEFAULT_INDEX
;
430 index
->lengths
= NULL
;
431 index
->values
= NULL
;
433 conf
->max_index_len
= sizeof(NGX_HTTP_DEFAULT_INDEX
);
443 ngx_http_index_init(ngx_conf_t
*cf
)
445 ngx_http_handler_pt
*h
;
446 ngx_http_core_main_conf_t
*cmcf
;
448 cmcf
= ngx_http_conf_get_module_main_conf(cf
, ngx_http_core_module
);
450 h
= ngx_array_push(&cmcf
->phases
[NGX_HTTP_CONTENT_PHASE
].handlers
);
455 *h
= ngx_http_index_handler
;
461 /* TODO: warn about duplicate indices */
464 ngx_http_index_set_index(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
466 ngx_http_index_loc_conf_t
*ilcf
= conf
;
470 ngx_http_index_t
*index
;
471 ngx_http_script_compile_t sc
;
473 if (ilcf
->indices
== NULL
) {
474 ilcf
->indices
= ngx_array_create(cf
->pool
, 2, sizeof(ngx_http_index_t
));
475 if (ilcf
->indices
== NULL
) {
476 return NGX_CONF_ERROR
;
480 value
= cf
->args
->elts
;
482 for (i
= 1; i
< cf
->args
->nelts
; i
++) {
484 if (value
[i
].data
[0] == '/' && i
!= cf
->args
->nelts
- 1) {
485 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
486 "only the last index in \"index\" directive "
487 "should be absolute");
490 if (value
[i
].len
== 0) {
491 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
492 "index \"%V\" in \"index\" directive is invalid",
494 return NGX_CONF_ERROR
;
497 index
= ngx_array_push(ilcf
->indices
);
499 return NGX_CONF_ERROR
;
502 index
->name
.len
= value
[i
].len
;
503 index
->name
.data
= value
[i
].data
;
504 index
->lengths
= NULL
;
505 index
->values
= NULL
;
507 n
= ngx_http_script_variables_count(&value
[i
]);
510 if (ilcf
->max_index_len
< index
->name
.len
) {
511 ilcf
->max_index_len
= index
->name
.len
;
514 if (index
->name
.data
[0] == '/') {
518 /* include the terminating '\0' to the length to use ngx_memcpy() */
524 ngx_memzero(&sc
, sizeof(ngx_http_script_compile_t
));
527 sc
.source
= &value
[i
];
528 sc
.lengths
= &index
->lengths
;
529 sc
.values
= &index
->values
;
531 sc
.complete_lengths
= 1;
532 sc
.complete_values
= 1;
534 if (ngx_http_script_compile(&sc
) != NGX_OK
) {
535 return NGX_CONF_ERROR
;