Annotation of embedaddon/nginx/src/http/modules/ngx_http_index_module.c, revision 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>