3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
20 ngx_array_t
*indices
; /* array of ngx_http_index_t */
22 } ngx_http_index_loc_conf_t
;
25 #define NGX_HTTP_DEFAULT_INDEX "index.html"
28 static ngx_int_t
ngx_http_index_test_dir(ngx_http_request_t
*r
,
29 ngx_http_core_loc_conf_t
*clcf
, u_char
*path
, u_char
*last
);
30 static ngx_int_t
ngx_http_index_error(ngx_http_request_t
*r
, u_char
*file
,
33 static ngx_int_t
ngx_http_index_init(ngx_conf_t
*cf
);
34 static void *ngx_http_index_create_loc_conf(ngx_conf_t
*cf
);
35 static char *ngx_http_index_merge_loc_conf(ngx_conf_t
*cf
,
36 void *parent
, void *child
);
37 static char *ngx_http_index_set_index(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
41 static ngx_command_t ngx_http_index_commands
[] = {
43 { ngx_string("index"),
44 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_1MORE
,
45 ngx_http_index_set_index
,
46 NGX_HTTP_LOC_CONF_OFFSET
,
54 static ngx_http_module_t ngx_http_index_module_ctx
= {
55 NULL
, /* preconfiguration */
56 ngx_http_index_init
, /* postconfiguration */
58 NULL
, /* create main configuration */
59 NULL
, /* init main configuration */
61 NULL
, /* create server configuration */
62 NULL
, /* merge server configuration */
64 ngx_http_index_create_loc_conf
, /* create location configration */
65 ngx_http_index_merge_loc_conf
/* merge location configration */
69 ngx_module_t ngx_http_index_module
= {
71 &ngx_http_index_module_ctx
, /* module context */
72 ngx_http_index_commands
, /* module directives */
73 NGX_HTTP_MODULE
, /* module type */
74 NULL
, /* init master */
75 NULL
, /* init module */
76 NULL
, /* init process */
77 NULL
, /* init thread */
78 NULL
, /* exit thread */
79 NULL
, /* exit process */
80 NULL
, /* exit master */
86 * Try to open the first index file before the test of the directory existence
87 * because the valid requests should be many more than invalid ones.
88 * If open() would fail, then stat() should be more quickly because some data
89 * is already cached in the kernel.
90 * Besides, Win32 has ERROR_PATH_NOT_FOUND (NGX_ENOTDIR).
91 * Unix has ENOTDIR error, although it less helpfull - it points only
92 * that path contains the usual file in place of the directory.
96 ngx_http_index_handler(ngx_http_request_t
*r
)
99 size_t len
, nlen
, root
, 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
))) {
121 if (r
->zero_in_uri
) {
125 log
= r
->connection
->log
;
127 ilcf
= ngx_http_get_module_loc_conf(r
, ngx_http_index_module
);
128 clcf
= ngx_http_get_module_loc_conf(r
, ngx_http_core_module
);
136 index
= ilcf
->indices
->elts
;
137 for (i
= 0; i
< ilcf
->indices
->nelts
; i
++) {
139 if (index
[i
].lengths
== NULL
) {
141 if (index
[i
].name
.data
[0] == '/') {
142 return ngx_http_internal_redirect(r
, &index
[i
].name
, &r
->args
);
145 len
= ilcf
->max_index_len
;
146 nlen
= index
[i
].name
.len
;
149 ngx_memzero(&e
, sizeof(ngx_http_script_engine_t
));
151 e
.ip
= index
[i
].lengths
->elts
;
155 /* 1 byte for terminating '\0' */
159 while (*(uintptr_t *) e
.ip
) {
160 lcode
= *(ngx_http_script_len_code_pt
*) e
.ip
;
166 /* 16 bytes are preallocation */
171 if (len
> (size_t) (path
.data
+ allocated
- name
)) {
173 name
= ngx_http_map_uri_to_path(r
, &path
, &root
, len
);
178 allocated
= path
.len
;
181 if (index
[i
].values
== NULL
) {
183 /* index[i].name.len includes the terminating '\0' */
185 ngx_memcpy(name
, index
[i
].name
.data
, index
[i
].name
.len
);
187 path
.len
= (name
+ index
[i
].name
.len
- 1) - path
.data
;
190 e
.ip
= index
[i
].values
->elts
;
193 while (*(uintptr_t *) e
.ip
) {
194 code
= *(ngx_http_script_code_pt
*) e
.ip
;
195 code((ngx_http_script_engine_t
*) &e
);
201 return ngx_http_internal_redirect(r
, &uri
, &r
->args
);
204 path
.len
= e
.pos
- path
.data
;
209 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, log
, 0, "open index \"%V\"", &path
);
211 ngx_memzero(&of
, sizeof(ngx_open_file_info_t
));
213 of
.valid
= clcf
->open_file_cache_valid
;
214 of
.min_uses
= clcf
->open_file_cache_min_uses
;
215 of
.errors
= clcf
->open_file_cache_errors
;
216 of
.events
= clcf
->open_file_cache_events
;
218 if (ngx_open_cached_file(clcf
->open_file_cache
, &path
, &of
, r
->pool
)
221 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, log
, of
.err
,
222 ngx_open_file_n
" \"%s\" failed", path
.data
);
225 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
228 if (of
.err
== NGX_ENOTDIR
|| of
.err
== NGX_EACCES
) {
229 return ngx_http_index_error(r
, path
.data
, of
.err
);
233 rc
= ngx_http_index_test_dir(r
, clcf
, path
.data
, name
- 1);
242 if (of
.err
== NGX_ENOENT
) {
246 ngx_log_error(NGX_LOG_ERR
, log
, of
.err
,
247 ngx_open_file_n
" \"%s\" failed", path
.data
);
249 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
252 uri
.len
= r
->uri
.len
+ nlen
- 1;
255 uri
.data
= path
.data
+ root
;
258 uri
.data
= ngx_pnalloc(r
->pool
, uri
.len
);
259 if (uri
.data
== NULL
) {
260 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
263 p
= ngx_copy(uri
.data
, r
->uri
.data
, r
->uri
.len
);
264 ngx_memcpy(p
, name
, nlen
- 1);
267 return ngx_http_internal_redirect(r
, &uri
, &r
->args
);
275 ngx_http_index_test_dir(ngx_http_request_t
*r
, ngx_http_core_loc_conf_t
*clcf
,
276 u_char
*path
, u_char
*last
)
280 ngx_open_file_info_t of
;
283 if (c
!= '/' || path
== last
) {
284 /* "alias" without trailing slash */
289 dir
.len
= last
- path
;
292 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
293 "http index check dir: \"%V\"", &dir
);
295 ngx_memzero(&of
, sizeof(ngx_open_file_info_t
));
298 of
.valid
= clcf
->open_file_cache_valid
;
299 of
.errors
= clcf
->open_file_cache_errors
;
301 if (ngx_open_cached_file(clcf
->open_file_cache
, &dir
, &of
, r
->pool
)
306 if (of
.err
== NGX_ENOENT
) {
308 return ngx_http_index_error(r
, dir
.data
, NGX_ENOENT
);
311 ngx_log_error(NGX_LOG_CRIT
, r
->connection
->log
, of
.err
,
312 ngx_open_file_n
" \"%s\" failed", dir
.data
);
315 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
324 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, 0,
325 "\"%s\" is not a directory", dir
.data
);
327 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
332 ngx_http_index_error(ngx_http_request_t
*r
, u_char
*file
, ngx_err_t err
)
334 if (err
== NGX_EACCES
) {
335 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, err
,
336 "\"%s\" is forbidden", file
);
338 return NGX_HTTP_FORBIDDEN
;
341 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, err
,
342 "\"%s\" is not found", file
);
344 return NGX_HTTP_NOT_FOUND
;
349 ngx_http_index_create_loc_conf(ngx_conf_t
*cf
)
351 ngx_http_index_loc_conf_t
*conf
;
353 conf
= ngx_palloc(cf
->pool
, sizeof(ngx_http_index_loc_conf_t
));
355 return NGX_CONF_ERROR
;
358 conf
->indices
= NULL
;
359 conf
->max_index_len
= 0;
366 ngx_http_index_merge_loc_conf(ngx_conf_t
*cf
, void *parent
, void *child
)
368 ngx_http_index_loc_conf_t
*prev
= parent
;
369 ngx_http_index_loc_conf_t
*conf
= child
;
371 ngx_http_index_t
*index
;
373 if (conf
->indices
== NULL
) {
374 conf
->indices
= prev
->indices
;
375 conf
->max_index_len
= prev
->max_index_len
;
378 if (conf
->indices
== NULL
) {
379 conf
->indices
= ngx_array_create(cf
->pool
, 1, sizeof(ngx_http_index_t
));
380 if (conf
->indices
== NULL
) {
381 return NGX_CONF_ERROR
;
384 index
= ngx_array_push(conf
->indices
);
386 return NGX_CONF_ERROR
;
389 index
->name
.len
= sizeof(NGX_HTTP_DEFAULT_INDEX
);
390 index
->name
.data
= (u_char
*) NGX_HTTP_DEFAULT_INDEX
;
391 index
->lengths
= NULL
;
392 index
->values
= NULL
;
394 conf
->max_index_len
= sizeof(NGX_HTTP_DEFAULT_INDEX
);
404 ngx_http_index_init(ngx_conf_t
*cf
)
406 ngx_http_handler_pt
*h
;
407 ngx_http_core_main_conf_t
*cmcf
;
409 cmcf
= ngx_http_conf_get_module_main_conf(cf
, ngx_http_core_module
);
411 h
= ngx_array_push(&cmcf
->phases
[NGX_HTTP_CONTENT_PHASE
].handlers
);
416 *h
= ngx_http_index_handler
;
422 /* TODO: warn about duplicate indices */
425 ngx_http_index_set_index(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
427 ngx_http_index_loc_conf_t
*ilcf
= conf
;
431 ngx_http_index_t
*index
;
432 ngx_http_script_compile_t sc
;
434 if (ilcf
->indices
== NULL
) {
435 ilcf
->indices
= ngx_array_create(cf
->pool
, 2, sizeof(ngx_http_index_t
));
436 if (ilcf
->indices
== NULL
) {
437 return NGX_CONF_ERROR
;
441 value
= cf
->args
->elts
;
443 for (i
= 1; i
< cf
->args
->nelts
; i
++) {
445 if (value
[i
].data
[0] == '/' && i
!= cf
->args
->nelts
- 1) {
446 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
447 "only the last index in \"index\" directive "
448 "should be absolute");
451 if (value
[i
].len
== 0) {
452 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
453 "index \"%V\" in \"index\" directive is invalid",
455 return NGX_CONF_ERROR
;
458 index
= ngx_array_push(ilcf
->indices
);
460 return NGX_CONF_ERROR
;
463 index
->name
.len
= value
[i
].len
;
464 index
->name
.data
= value
[i
].data
;
465 index
->lengths
= NULL
;
466 index
->values
= NULL
;
468 n
= ngx_http_script_variables_count(&value
[i
]);
471 if (ilcf
->max_index_len
< index
->name
.len
) {
472 ilcf
->max_index_len
= index
->name
.len
;
475 if (index
->name
.data
[0] == '/') {
479 /* include the terminating '\0' to the length to use ngx_copy() */
485 ngx_memzero(&sc
, sizeof(ngx_http_script_compile_t
));
488 sc
.source
= &value
[i
];
489 sc
.lengths
= &index
->lengths
;
490 sc
.values
= &index
->values
;
492 sc
.complete_lengths
= 1;
493 sc
.complete_values
= 1;
495 if (ngx_http_script_compile(&sc
) != NGX_OK
) {
496 return NGX_CONF_ERROR
;