Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 typedef struct {
0014     ngx_str_t                name;
0015     ngx_array_t             *lengths;
0016     ngx_array_t             *values;
0017 } ngx_http_index_t;
0018 
0019 
0020 typedef struct {
0021     ngx_array_t             *indices;    /* array of ngx_http_index_t */
0022     size_t                   max_index_len;
0023 } ngx_http_index_loc_conf_t;
0024 
0025 
0026 #define NGX_HTTP_DEFAULT_INDEX   "index.html"
0027 
0028 
0029 static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r,
0030     ngx_http_core_loc_conf_t *clcf, u_char *path, u_char *last);
0031 static ngx_int_t ngx_http_index_error(ngx_http_request_t *r,
0032     ngx_http_core_loc_conf_t *clcf, u_char *file, ngx_err_t err);
0033 
0034 static ngx_int_t ngx_http_index_init(ngx_conf_t *cf);
0035 static void *ngx_http_index_create_loc_conf(ngx_conf_t *cf);
0036 static char *ngx_http_index_merge_loc_conf(ngx_conf_t *cf,
0037     void *parent, void *child);
0038 static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd,
0039     void *conf);
0040 
0041 
0042 static ngx_command_t  ngx_http_index_commands[] = {
0043 
0044     { ngx_string("index"),
0045       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
0046       ngx_http_index_set_index,
0047       NGX_HTTP_LOC_CONF_OFFSET,
0048       0,
0049       NULL },
0050 
0051       ngx_null_command
0052 };
0053 
0054 
0055 static ngx_http_module_t  ngx_http_index_module_ctx = {
0056     NULL,                                  /* preconfiguration */
0057     ngx_http_index_init,                   /* postconfiguration */
0058 
0059     NULL,                                  /* create main configuration */
0060     NULL,                                  /* init main configuration */
0061 
0062     NULL,                                  /* create server configuration */
0063     NULL,                                  /* merge server configuration */
0064 
0065     ngx_http_index_create_loc_conf,        /* create location configuration */
0066     ngx_http_index_merge_loc_conf          /* merge location configuration */
0067 };
0068 
0069 
0070 ngx_module_t  ngx_http_index_module = {
0071     NGX_MODULE_V1,
0072     &ngx_http_index_module_ctx,            /* module context */
0073     ngx_http_index_commands,               /* module directives */
0074     NGX_HTTP_MODULE,                       /* module type */
0075     NULL,                                  /* init master */
0076     NULL,                                  /* init module */
0077     NULL,                                  /* init process */
0078     NULL,                                  /* init thread */
0079     NULL,                                  /* exit thread */
0080     NULL,                                  /* exit process */
0081     NULL,                                  /* exit master */
0082     NGX_MODULE_V1_PADDING
0083 };
0084 
0085 
0086 /*
0087  * Try to open/test the first index file before the test of directory
0088  * existence because valid requests should prevail over invalid ones.
0089  * If open()/stat() of a file will fail then stat() of a directory
0090  * should be faster because kernel may have already cached some data.
0091  * Besides, Win32 may return ERROR_PATH_NOT_FOUND (NGX_ENOTDIR) at once.
0092  * Unix has ENOTDIR error; however, it's less helpful than Win32's one:
0093  * it only indicates that path points to a regular file, not a directory.
0094  */
0095 
0096 static ngx_int_t
0097 ngx_http_index_handler(ngx_http_request_t *r)
0098 {
0099     u_char                       *p, *name;
0100     size_t                        len, root, reserve, allocated;
0101     ngx_int_t                     rc;
0102     ngx_str_t                     path, uri;
0103     ngx_uint_t                    i, dir_tested;
0104     ngx_http_index_t             *index;
0105     ngx_open_file_info_t          of;
0106     ngx_http_script_code_pt       code;
0107     ngx_http_script_engine_t      e;
0108     ngx_http_core_loc_conf_t     *clcf;
0109     ngx_http_index_loc_conf_t    *ilcf;
0110     ngx_http_script_len_code_pt   lcode;
0111 
0112     if (r->uri.data[r->uri.len - 1] != '/') {
0113         return NGX_DECLINED;
0114     }
0115 
0116     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
0117         return NGX_DECLINED;
0118     }
0119 
0120     ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
0121     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
0122 
0123     allocated = 0;
0124     root = 0;
0125     dir_tested = 0;
0126     name = NULL;
0127     /* suppress MSVC warning */
0128     path.data = NULL;
0129 
0130     index = ilcf->indices->elts;
0131     for (i = 0; i < ilcf->indices->nelts; i++) {
0132 
0133         if (index[i].lengths == NULL) {
0134 
0135             if (index[i].name.data[0] == '/') {
0136                 return ngx_http_internal_redirect(r, &index[i].name, &r->args);
0137             }
0138 
0139             reserve = ilcf->max_index_len;
0140             len = index[i].name.len;
0141 
0142         } else {
0143             ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
0144 
0145             e.ip = index[i].lengths->elts;
0146             e.request = r;
0147             e.flushed = 1;
0148 
0149             /* 1 is for terminating '\0' as in static names */
0150             len = 1;
0151 
0152             while (*(uintptr_t *) e.ip) {
0153                 lcode = *(ngx_http_script_len_code_pt *) e.ip;
0154                 len += lcode(&e);
0155             }
0156 
0157             /* 16 bytes are preallocation */
0158 
0159             reserve = len + 16;
0160         }
0161 
0162         if (reserve > allocated) {
0163 
0164             name = ngx_http_map_uri_to_path(r, &path, &root, reserve);
0165             if (name == NULL) {
0166                 return NGX_ERROR;
0167             }
0168 
0169             allocated = path.data + path.len - name;
0170         }
0171 
0172         if (index[i].values == NULL) {
0173 
0174             /* index[i].name.len includes the terminating '\0' */
0175 
0176             ngx_memcpy(name, index[i].name.data, index[i].name.len);
0177 
0178             path.len = (name + index[i].name.len - 1) - path.data;
0179 
0180         } else {
0181             e.ip = index[i].values->elts;
0182             e.pos = name;
0183 
0184             while (*(uintptr_t *) e.ip) {
0185                 code = *(ngx_http_script_code_pt *) e.ip;
0186                 code((ngx_http_script_engine_t *) &e);
0187             }
0188 
0189             if (*name == '/') {
0190                 uri.len = len - 1;
0191                 uri.data = name;
0192                 return ngx_http_internal_redirect(r, &uri, &r->args);
0193             }
0194 
0195             path.len = e.pos - path.data;
0196 
0197             *e.pos = '\0';
0198         }
0199 
0200         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0201                        "open index \"%V\"", &path);
0202 
0203         ngx_memzero(&of, sizeof(ngx_open_file_info_t));
0204 
0205         of.read_ahead = clcf->read_ahead;
0206         of.directio = clcf->directio;
0207         of.valid = clcf->open_file_cache_valid;
0208         of.min_uses = clcf->open_file_cache_min_uses;
0209         of.test_only = 1;
0210         of.errors = clcf->open_file_cache_errors;
0211         of.events = clcf->open_file_cache_events;
0212 
0213         if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
0214             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0215         }
0216 
0217         if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
0218             != NGX_OK)
0219         {
0220             if (of.err == 0) {
0221                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
0222             }
0223 
0224             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
0225                            "%s \"%s\" failed", of.failed, path.data);
0226 
0227 #if (NGX_HAVE_OPENAT)
0228             if (of.err == NGX_EMLINK
0229                 || of.err == NGX_ELOOP)
0230             {
0231                 return NGX_HTTP_FORBIDDEN;
0232             }
0233 #endif
0234 
0235             if (of.err == NGX_ENOTDIR
0236                 || of.err == NGX_ENAMETOOLONG
0237                 || of.err == NGX_EACCES)
0238             {
0239                 return ngx_http_index_error(r, clcf, path.data, of.err);
0240             }
0241 
0242             if (!dir_tested) {
0243                 rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);
0244 
0245                 if (rc != NGX_OK) {
0246                     return rc;
0247                 }
0248 
0249                 dir_tested = 1;
0250             }
0251 
0252             if (of.err == NGX_ENOENT) {
0253                 continue;
0254             }
0255 
0256             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
0257                           "%s \"%s\" failed", of.failed, path.data);
0258 
0259             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0260         }
0261 
0262         uri.len = r->uri.len + len - 1;
0263 
0264         if (!clcf->alias) {
0265             uri.data = path.data + root;
0266 
0267         } else {
0268             uri.data = ngx_pnalloc(r->pool, uri.len);
0269             if (uri.data == NULL) {
0270                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
0271             }
0272 
0273             p = ngx_copy(uri.data, r->uri.data, r->uri.len);
0274             ngx_memcpy(p, name, len - 1);
0275         }
0276 
0277         return ngx_http_internal_redirect(r, &uri, &r->args);
0278     }
0279 
0280     return NGX_DECLINED;
0281 }
0282 
0283 
0284 static ngx_int_t
0285 ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,
0286     u_char *path, u_char *last)
0287 {
0288     u_char                c;
0289     ngx_str_t             dir;
0290     ngx_open_file_info_t  of;
0291 
0292     c = *last;
0293     if (c != '/' || path == last) {
0294         /* "alias" without trailing slash */
0295         c = *(++last);
0296     }
0297     *last = '\0';
0298 
0299     dir.len = last - path;
0300     dir.data = path;
0301 
0302     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0303                    "http index check dir: \"%V\"", &dir);
0304 
0305     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
0306 
0307     of.test_dir = 1;
0308     of.test_only = 1;
0309     of.valid = clcf->open_file_cache_valid;
0310     of.errors = clcf->open_file_cache_errors;
0311 
0312     if (ngx_http_set_disable_symlinks(r, clcf, &dir, &of) != NGX_OK) {
0313         return NGX_HTTP_INTERNAL_SERVER_ERROR;
0314     }
0315 
0316     if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)
0317         != NGX_OK)
0318     {
0319         if (of.err) {
0320 
0321 #if (NGX_HAVE_OPENAT)
0322             if (of.err == NGX_EMLINK
0323                 || of.err == NGX_ELOOP)
0324             {
0325                 return NGX_HTTP_FORBIDDEN;
0326             }
0327 #endif
0328 
0329             if (of.err == NGX_ENOENT) {
0330                 *last = c;
0331                 return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT);
0332             }
0333 
0334             if (of.err == NGX_EACCES) {
0335 
0336                 *last = c;
0337 
0338                 /*
0339                  * ngx_http_index_test_dir() is called after the first index
0340                  * file testing has returned an error distinct from NGX_EACCES.
0341                  * This means that directory searching is allowed.
0342                  */
0343 
0344                 return NGX_OK;
0345             }
0346 
0347             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
0348                           "%s \"%s\" failed", of.failed, dir.data);
0349         }
0350 
0351         return NGX_HTTP_INTERNAL_SERVER_ERROR;
0352     }
0353 
0354     *last = c;
0355 
0356     if (of.is_dir) {
0357         return NGX_OK;
0358     }
0359 
0360     ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
0361                   "\"%s\" is not a directory", dir.data);
0362 
0363     return NGX_HTTP_INTERNAL_SERVER_ERROR;
0364 }
0365 
0366 
0367 static ngx_int_t
0368 ngx_http_index_error(ngx_http_request_t *r, ngx_http_core_loc_conf_t  *clcf,
0369     u_char *file, ngx_err_t err)
0370 {
0371     if (err == NGX_EACCES) {
0372         ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
0373                       "\"%s\" is forbidden", file);
0374 
0375         return NGX_HTTP_FORBIDDEN;
0376     }
0377 
0378     if (clcf->log_not_found) {
0379         ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
0380                       "\"%s\" is not found", file);
0381     }
0382 
0383     return NGX_HTTP_NOT_FOUND;
0384 }
0385 
0386 
0387 static void *
0388 ngx_http_index_create_loc_conf(ngx_conf_t *cf)
0389 {
0390     ngx_http_index_loc_conf_t  *conf;
0391 
0392     conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t));
0393     if (conf == NULL) {
0394         return NULL;
0395     }
0396 
0397     conf->indices = NULL;
0398     conf->max_index_len = 0;
0399 
0400     return conf;
0401 }
0402 
0403 
0404 static char *
0405 ngx_http_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
0406 {
0407     ngx_http_index_loc_conf_t  *prev = parent;
0408     ngx_http_index_loc_conf_t  *conf = child;
0409 
0410     ngx_http_index_t  *index;
0411 
0412     if (conf->indices == NULL) {
0413         conf->indices = prev->indices;
0414         conf->max_index_len = prev->max_index_len;
0415     }
0416 
0417     if (conf->indices == NULL) {
0418         conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t));
0419         if (conf->indices == NULL) {
0420             return NGX_CONF_ERROR;
0421         }
0422 
0423         index = ngx_array_push(conf->indices);
0424         if (index == NULL) {
0425             return NGX_CONF_ERROR;
0426         }
0427 
0428         index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX);
0429         index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX;
0430         index->lengths = NULL;
0431         index->values = NULL;
0432 
0433         conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX);
0434 
0435         return NGX_CONF_OK;
0436     }
0437 
0438     return NGX_CONF_OK;
0439 }
0440 
0441 
0442 static ngx_int_t
0443 ngx_http_index_init(ngx_conf_t *cf)
0444 {
0445     ngx_http_handler_pt        *h;
0446     ngx_http_core_main_conf_t  *cmcf;
0447 
0448     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
0449 
0450     h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
0451     if (h == NULL) {
0452         return NGX_ERROR;
0453     }
0454 
0455     *h = ngx_http_index_handler;
0456 
0457     return NGX_OK;
0458 }
0459 
0460 
0461 /* TODO: warn about duplicate indices */
0462 
0463 static char *
0464 ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0465 {
0466     ngx_http_index_loc_conf_t *ilcf = conf;
0467 
0468     ngx_str_t                  *value;
0469     ngx_uint_t                  i, n;
0470     ngx_http_index_t           *index;
0471     ngx_http_script_compile_t   sc;
0472 
0473     if (ilcf->indices == NULL) {
0474         ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t));
0475         if (ilcf->indices == NULL) {
0476             return NGX_CONF_ERROR;
0477         }
0478     }
0479 
0480     value = cf->args->elts;
0481 
0482     for (i = 1; i < cf->args->nelts; i++) {
0483 
0484         if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {
0485             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
0486                                "only the last index in \"index\" directive "
0487                                "should be absolute");
0488         }
0489 
0490         if (value[i].len == 0) {
0491             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0492                                "index \"%V\" in \"index\" directive is invalid",
0493                                &value[1]);
0494             return NGX_CONF_ERROR;
0495         }
0496 
0497         index = ngx_array_push(ilcf->indices);
0498         if (index == NULL) {
0499             return NGX_CONF_ERROR;
0500         }
0501 
0502         index->name.len = value[i].len;
0503         index->name.data = value[i].data;
0504         index->lengths = NULL;
0505         index->values = NULL;
0506 
0507         n = ngx_http_script_variables_count(&value[i]);
0508 
0509         if (n == 0) {
0510             if (ilcf->max_index_len < index->name.len) {
0511                 ilcf->max_index_len = index->name.len;
0512             }
0513 
0514             if (index->name.data[0] == '/') {
0515                 continue;
0516             }
0517 
0518             /* include the terminating '\0' to the length to use ngx_memcpy() */
0519             index->name.len++;
0520 
0521             continue;
0522         }
0523 
0524         ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
0525 
0526         sc.cf = cf;
0527         sc.source = &value[i];
0528         sc.lengths = &index->lengths;
0529         sc.values = &index->values;
0530         sc.variables = n;
0531         sc.complete_lengths = 1;
0532         sc.complete_values = 1;
0533 
0534         if (ngx_http_script_compile(&sc) != NGX_OK) {
0535             return NGX_CONF_ERROR;
0536         }
0537     }
0538 
0539     return NGX_CONF_OK;
0540 }