Annotation of embedaddon/nginx/src/http/modules/ngx_http_index_module.c, revision 1.1.1.1
1.1 misho 1:
2: /*
3: * Copyright (C) Igor Sysoev
4: * Copyright (C) Nginx, Inc.
5: */
6:
7:
8: #include <ngx_config.h>
9: #include <ngx_core.h>
10: #include <ngx_http.h>
11:
12:
13: typedef struct {
14: ngx_str_t name;
15: ngx_array_t *lengths;
16: ngx_array_t *values;
17: } ngx_http_index_t;
18:
19:
20: typedef struct {
21: ngx_array_t *indices; /* array of ngx_http_index_t */
22: size_t max_index_len;
23: } ngx_http_index_loc_conf_t;
24:
25:
26: #define NGX_HTTP_DEFAULT_INDEX "index.html"
27:
28:
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);
33:
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,
39: void *conf);
40:
41:
42: static ngx_command_t ngx_http_index_commands[] = {
43:
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,
48: 0,
49: NULL },
50:
51: ngx_null_command
52: };
53:
54:
55: static ngx_http_module_t ngx_http_index_module_ctx = {
56: NULL, /* preconfiguration */
57: ngx_http_index_init, /* postconfiguration */
58:
59: NULL, /* create main configuration */
60: NULL, /* init main configuration */
61:
62: NULL, /* create server configuration */
63: NULL, /* merge server configuration */
64:
65: ngx_http_index_create_loc_conf, /* create location configuration */
66: ngx_http_index_merge_loc_conf /* merge location configuration */
67: };
68:
69:
70: ngx_module_t ngx_http_index_module = {
71: NGX_MODULE_V1,
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 */
82: NGX_MODULE_V1_PADDING
83: };
84:
85:
86: /*
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.
94: */
95:
96: static ngx_int_t
97: ngx_http_index_handler(ngx_http_request_t *r)
98: {
99: u_char *p, *name;
100: size_t len, root, reserve, allocated;
101: ngx_int_t rc;
102: ngx_str_t path, uri;
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;
111:
112: if (r->uri.data[r->uri.len - 1] != '/') {
113: return NGX_DECLINED;
114: }
115:
116: if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
117: return NGX_DECLINED;
118: }
119:
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);
122:
123: allocated = 0;
124: root = 0;
125: dir_tested = 0;
126: name = NULL;
127: /* suppress MSVC warning */
128: path.data = NULL;
129:
130: index = ilcf->indices->elts;
131: for (i = 0; i < ilcf->indices->nelts; i++) {
132:
133: if (index[i].lengths == NULL) {
134:
135: if (index[i].name.data[0] == '/') {
136: return ngx_http_internal_redirect(r, &index[i].name, &r->args);
137: }
138:
139: reserve = ilcf->max_index_len;
140: len = index[i].name.len;
141:
142: } else {
143: ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
144:
145: e.ip = index[i].lengths->elts;
146: e.request = r;
147: e.flushed = 1;
148:
149: /* 1 is for terminating '\0' as in static names */
150: len = 1;
151:
152: while (*(uintptr_t *) e.ip) {
153: lcode = *(ngx_http_script_len_code_pt *) e.ip;
154: len += lcode(&e);
155: }
156:
157: /* 16 bytes are preallocation */
158:
159: reserve = len + 16;
160: }
161:
162: if (reserve > allocated) {
163:
164: name = ngx_http_map_uri_to_path(r, &path, &root, reserve);
165: if (name == NULL) {
166: return NGX_ERROR;
167: }
168:
169: allocated = path.data + path.len - name;
170: }
171:
172: if (index[i].values == NULL) {
173:
174: /* index[i].name.len includes the terminating '\0' */
175:
176: ngx_memcpy(name, index[i].name.data, index[i].name.len);
177:
178: path.len = (name + index[i].name.len - 1) - path.data;
179:
180: } else {
181: e.ip = index[i].values->elts;
182: e.pos = name;
183:
184: while (*(uintptr_t *) e.ip) {
185: code = *(ngx_http_script_code_pt *) e.ip;
186: code((ngx_http_script_engine_t *) &e);
187: }
188:
189: if (*name == '/') {
190: uri.len = len - 1;
191: uri.data = name;
192: return ngx_http_internal_redirect(r, &uri, &r->args);
193: }
194:
195: path.len = e.pos - path.data;
196:
197: *e.pos = '\0';
198: }
199:
200: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
201: "open index \"%V\"", &path);
202:
203: ngx_memzero(&of, sizeof(ngx_open_file_info_t));
204:
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;
209: of.test_only = 1;
210: of.errors = clcf->open_file_cache_errors;
211: of.events = clcf->open_file_cache_events;
212:
213: if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
214: return NGX_HTTP_INTERNAL_SERVER_ERROR;
215: }
216:
217: if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
218: != NGX_OK)
219: {
220: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
221: "%s \"%s\" failed", of.failed, path.data);
222:
223: if (of.err == 0) {
224: return NGX_HTTP_INTERNAL_SERVER_ERROR;
225: }
226:
227: #if (NGX_HAVE_OPENAT)
228: if (of.err == NGX_EMLINK
229: || of.err == NGX_ELOOP)
230: {
231: return NGX_HTTP_FORBIDDEN;
232: }
233: #endif
234:
235: if (of.err == NGX_ENOTDIR
236: || of.err == NGX_ENAMETOOLONG
237: || of.err == NGX_EACCES)
238: {
239: return ngx_http_index_error(r, clcf, path.data, of.err);
240: }
241:
242: if (!dir_tested) {
243: rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);
244:
245: if (rc != NGX_OK) {
246: return rc;
247: }
248:
249: dir_tested = 1;
250: }
251:
252: if (of.err == NGX_ENOENT) {
253: continue;
254: }
255:
256: ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
257: "%s \"%s\" failed", of.failed, path.data);
258:
259: return NGX_HTTP_INTERNAL_SERVER_ERROR;
260: }
261:
262: uri.len = r->uri.len + len - 1;
263:
264: if (!clcf->alias) {
265: uri.data = path.data + root;
266:
267: } else {
268: uri.data = ngx_pnalloc(r->pool, uri.len);
269: if (uri.data == NULL) {
270: return NGX_HTTP_INTERNAL_SERVER_ERROR;
271: }
272:
273: p = ngx_copy(uri.data, r->uri.data, r->uri.len);
274: ngx_memcpy(p, name, len - 1);
275: }
276:
277: return ngx_http_internal_redirect(r, &uri, &r->args);
278: }
279:
280: return NGX_DECLINED;
281: }
282:
283:
284: static ngx_int_t
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)
287: {
288: u_char c;
289: ngx_str_t dir;
290: ngx_open_file_info_t of;
291:
292: c = *last;
293: if (c != '/' || path == last) {
294: /* "alias" without trailing slash */
295: c = *(++last);
296: }
297: *last = '\0';
298:
299: dir.len = last - path;
300: dir.data = path;
301:
302: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
303: "http index check dir: \"%V\"", &dir);
304:
305: ngx_memzero(&of, sizeof(ngx_open_file_info_t));
306:
307: of.test_dir = 1;
308: of.test_only = 1;
309: of.valid = clcf->open_file_cache_valid;
310: of.errors = clcf->open_file_cache_errors;
311:
312: if (ngx_http_set_disable_symlinks(r, clcf, &dir, &of) != NGX_OK) {
313: return NGX_HTTP_INTERNAL_SERVER_ERROR;
314: }
315:
316: if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)
317: != NGX_OK)
318: {
319: if (of.err) {
320:
321: #if (NGX_HAVE_OPENAT)
322: if (of.err == NGX_EMLINK
323: || of.err == NGX_ELOOP)
324: {
325: return NGX_HTTP_FORBIDDEN;
326: }
327: #endif
328:
329: if (of.err == NGX_ENOENT) {
330: *last = c;
331: return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT);
332: }
333:
334: if (of.err == NGX_EACCES) {
335:
336: *last = c;
337:
338: /*
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.
342: */
343:
344: return NGX_OK;
345: }
346:
347: ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
348: "%s \"%s\" failed", of.failed, dir.data);
349: }
350:
351: return NGX_HTTP_INTERNAL_SERVER_ERROR;
352: }
353:
354: *last = c;
355:
356: if (of.is_dir) {
357: return NGX_OK;
358: }
359:
360: ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
361: "\"%s\" is not a directory", dir.data);
362:
363: return NGX_HTTP_INTERNAL_SERVER_ERROR;
364: }
365:
366:
367: static ngx_int_t
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)
370: {
371: if (err == NGX_EACCES) {
372: ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
373: "\"%s\" is forbidden", file);
374:
375: return NGX_HTTP_FORBIDDEN;
376: }
377:
378: if (clcf->log_not_found) {
379: ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
380: "\"%s\" is not found", file);
381: }
382:
383: return NGX_HTTP_NOT_FOUND;
384: }
385:
386:
387: static void *
388: ngx_http_index_create_loc_conf(ngx_conf_t *cf)
389: {
390: ngx_http_index_loc_conf_t *conf;
391:
392: conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t));
393: if (conf == NULL) {
394: return NULL;
395: }
396:
397: conf->indices = NULL;
398: conf->max_index_len = 0;
399:
400: return conf;
401: }
402:
403:
404: static char *
405: ngx_http_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
406: {
407: ngx_http_index_loc_conf_t *prev = parent;
408: ngx_http_index_loc_conf_t *conf = child;
409:
410: ngx_http_index_t *index;
411:
412: if (conf->indices == NULL) {
413: conf->indices = prev->indices;
414: conf->max_index_len = prev->max_index_len;
415: }
416:
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;
421: }
422:
423: index = ngx_array_push(conf->indices);
424: if (index == NULL) {
425: return NGX_CONF_ERROR;
426: }
427:
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;
432:
433: conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX);
434:
435: return NGX_CONF_OK;
436: }
437:
438: return NGX_CONF_OK;
439: }
440:
441:
442: static ngx_int_t
443: ngx_http_index_init(ngx_conf_t *cf)
444: {
445: ngx_http_handler_pt *h;
446: ngx_http_core_main_conf_t *cmcf;
447:
448: cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
449:
450: h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
451: if (h == NULL) {
452: return NGX_ERROR;
453: }
454:
455: *h = ngx_http_index_handler;
456:
457: return NGX_OK;
458: }
459:
460:
461: /* TODO: warn about duplicate indices */
462:
463: static char *
464: ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
465: {
466: ngx_http_index_loc_conf_t *ilcf = conf;
467:
468: ngx_str_t *value;
469: ngx_uint_t i, n;
470: ngx_http_index_t *index;
471: ngx_http_script_compile_t sc;
472:
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;
477: }
478: }
479:
480: value = cf->args->elts;
481:
482: for (i = 1; i < cf->args->nelts; i++) {
483:
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");
488: }
489:
490: if (value[i].len == 0) {
491: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
492: "index \"%V\" in \"index\" directive is invalid",
493: &value[1]);
494: return NGX_CONF_ERROR;
495: }
496:
497: index = ngx_array_push(ilcf->indices);
498: if (index == NULL) {
499: return NGX_CONF_ERROR;
500: }
501:
502: index->name.len = value[i].len;
503: index->name.data = value[i].data;
504: index->lengths = NULL;
505: index->values = NULL;
506:
507: n = ngx_http_script_variables_count(&value[i]);
508:
509: if (n == 0) {
510: if (ilcf->max_index_len < index->name.len) {
511: ilcf->max_index_len = index->name.len;
512: }
513:
514: if (index->name.data[0] == '/') {
515: continue;
516: }
517:
518: /* include the terminating '\0' to the length to use ngx_memcpy() */
519: index->name.len++;
520:
521: continue;
522: }
523:
524: ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
525:
526: sc.cf = cf;
527: sc.source = &value[i];
528: sc.lengths = &index->lengths;
529: sc.values = &index->values;
530: sc.variables = n;
531: sc.complete_lengths = 1;
532: sc.complete_values = 1;
533:
534: if (ngx_http_script_compile(&sc) != NGX_OK) {
535: return NGX_CONF_ERROR;
536: }
537: }
538:
539: return NGX_CONF_OK;
540: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>