src/http/modules/ngx_http_index_module.c - nginx-1.31.4 nginx/ @ 8d9666701

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. typedef struct {
  9.     ngx_str_t                name;
  10.     ngx_array_t             *lengths;
  11.     ngx_array_t             *values;
  12. } ngx_http_index_t;


  13. typedef struct {
  14.     ngx_array_t             *indices;    /* array of ngx_http_index_t */
  15.     size_t                   max_index_len;
  16. } ngx_http_index_loc_conf_t;


  17. #define NGX_HTTP_DEFAULT_INDEX   "index.html"


  18. static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r,
  19.     ngx_http_core_loc_conf_t *clcf, u_char *path, u_char *last);
  20. static ngx_int_t ngx_http_index_error(ngx_http_request_t *r,
  21.     ngx_http_core_loc_conf_t *clcf, u_char *file, ngx_err_t err);

  22. static ngx_int_t ngx_http_index_init(ngx_conf_t *cf);
  23. static void *ngx_http_index_create_loc_conf(ngx_conf_t *cf);
  24. static char *ngx_http_index_merge_loc_conf(ngx_conf_t *cf,
  25.     void *parent, void *child);
  26. static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd,
  27.     void *conf);


  28. static ngx_command_t  ngx_http_index_commands[] = {

  29.     { ngx_string("index"),
  30.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  31.       ngx_http_index_set_index,
  32.       NGX_HTTP_LOC_CONF_OFFSET,
  33.       0,
  34.       NULL },

  35.       ngx_null_command
  36. };


  37. static ngx_http_module_t  ngx_http_index_module_ctx = {
  38.     NULL,                                  /* preconfiguration */
  39.     ngx_http_index_init,                   /* postconfiguration */

  40.     NULL,                                  /* create main configuration */
  41.     NULL,                                  /* init main configuration */

  42.     NULL,                                  /* create server configuration */
  43.     NULL,                                  /* merge server configuration */

  44.     ngx_http_index_create_loc_conf,        /* create location configuration */
  45.     ngx_http_index_merge_loc_conf          /* merge location configuration */
  46. };


  47. ngx_module_t  ngx_http_index_module = {
  48.     NGX_MODULE_V1,
  49.     &ngx_http_index_module_ctx,            /* module context */
  50.     ngx_http_index_commands,               /* module directives */
  51.     NGX_HTTP_MODULE,                       /* module type */
  52.     NULL,                                  /* init master */
  53.     NULL,                                  /* init module */
  54.     NULL,                                  /* init process */
  55.     NULL,                                  /* init thread */
  56.     NULL,                                  /* exit thread */
  57.     NULL,                                  /* exit process */
  58.     NULL,                                  /* exit master */
  59.     NGX_MODULE_V1_PADDING
  60. };


  61. /*
  62. * Try to open/test the first index file before the test of directory
  63. * existence because valid requests should prevail over invalid ones.
  64. * If open()/stat() of a file will fail then stat() of a directory
  65. * should be faster because kernel may have already cached some data.
  66. * Besides, Win32 may return ERROR_PATH_NOT_FOUND (NGX_ENOTDIR) at once.
  67. * Unix has ENOTDIR error; however, it's less helpful than Win32's one:
  68. * it only indicates that path points to a regular file, not a directory.
  69. */

  70. static ngx_int_t
  71. ngx_http_index_handler(ngx_http_request_t *r)
  72. {
  73.     u_char                       *p, *name;
  74.     size_t                        len, root, reserve, allocated;
  75.     ngx_int_t                     rc;
  76.     ngx_str_t                     path, uri;
  77.     ngx_uint_t                    i, dir_tested;
  78.     ngx_http_index_t             *index;
  79.     ngx_open_file_info_t          of;
  80.     ngx_http_script_code_pt       code;
  81.     ngx_http_script_engine_t      e;
  82.     ngx_http_core_loc_conf_t     *clcf;
  83.     ngx_http_index_loc_conf_t    *ilcf;
  84.     ngx_http_script_len_code_pt   lcode;

  85.     if (r->uri.data[r->uri.len - 1] != '/') {
  86.         return NGX_DECLINED;
  87.     }

  88.     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
  89.         return NGX_DECLINED;
  90.     }

  91.     ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
  92.     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

  93.     allocated = 0;
  94.     root = 0;
  95.     dir_tested = 0;
  96.     name = NULL;
  97.     /* suppress MSVC warning */
  98.     path.data = NULL;
  99.     e.status = 0;

  100.     index = ilcf->indices->elts;
  101.     for (i = 0; i < ilcf->indices->nelts; i++) {

  102.         if (index[i].lengths == NULL) {

  103.             if (index[i].name.data[0] == '/') {
  104.                 return ngx_http_internal_redirect(r, &index[i].name, &r->args);
  105.             }

  106.             reserve = ilcf->max_index_len;
  107.             len = index[i].name.len;

  108.         } else {
  109.             ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

  110.             e.ip = index[i].lengths->elts;
  111.             e.request = r;
  112.             e.flushed = 1;

  113.             /* 1 is for terminating '\0' as in static names */
  114.             len = 1;

  115.             while (*(uintptr_t *) e.ip) {
  116.                 lcode = *(ngx_http_script_len_code_pt *) e.ip;
  117.                 len += lcode(&e);
  118.             }

  119.             /* 16 bytes are preallocation */

  120.             reserve = len + 16;
  121.         }

  122.         if (reserve > allocated) {

  123.             name = ngx_http_map_uri_to_path(r, &path, &root, reserve);
  124.             if (name == NULL) {
  125.                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
  126.             }

  127.             allocated = path.data + path.len - name;
  128.         }

  129.         if (index[i].values == NULL) {

  130.             /* index[i].name.len includes the terminating '\0' */

  131.             ngx_memcpy(name, index[i].name.data, index[i].name.len);

  132.             path.len = (name + index[i].name.len - 1) - path.data;

  133.         } else {
  134.             e.ip = index[i].values->elts;
  135.             e.pos = name;
  136.             e.end = name + allocated;

  137.             while (*(uintptr_t *) e.ip) {
  138.                 code = *(ngx_http_script_code_pt *) e.ip;
  139.                 code((ngx_http_script_engine_t *) &e);
  140.             }

  141.             if (e.status) {
  142.                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
  143.             }

  144.             if (ngx_http_script_check_length(&e, 1) != NGX_OK) {
  145.                 return NGX_ERROR;
  146.             }

  147.             if (*name == '/') {
  148.                 uri.len = e.pos - name;
  149.                 uri.data = name;
  150.                 return ngx_http_internal_redirect(r, &uri, &r->args);
  151.             }

  152.             len = e.pos - name + 1;
  153.             path.len = e.pos - path.data;

  154.             *e.pos = '\0';
  155.         }

  156.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  157.                        "open index \"%V\"", &path);

  158.         ngx_memzero(&of, sizeof(ngx_open_file_info_t));

  159.         of.read_ahead = clcf->read_ahead;
  160.         of.directio = clcf->directio;
  161.         of.valid = clcf->open_file_cache_valid;
  162.         of.min_uses = clcf->open_file_cache_min_uses;
  163.         of.test_only = 1;
  164.         of.errors = clcf->open_file_cache_errors;
  165.         of.events = clcf->open_file_cache_events;

  166.         if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
  167.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  168.         }

  169.         if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
  170.             != NGX_OK)
  171.         {
  172.             if (of.err == 0) {
  173.                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
  174.             }

  175.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
  176.                            "%s \"%s\" failed", of.failed, path.data);

  177. #if (NGX_HAVE_OPENAT)
  178.             if (of.err == NGX_EMLINK
  179.                 || of.err == NGX_ELOOP)
  180.             {
  181.                 return NGX_HTTP_FORBIDDEN;
  182.             }
  183. #endif

  184.             if (of.err == NGX_ENOTDIR
  185.                 || of.err == NGX_ENAMETOOLONG
  186.                 || of.err == NGX_EACCES)
  187.             {
  188.                 return ngx_http_index_error(r, clcf, path.data, of.err);
  189.             }

  190.             if (!dir_tested) {
  191.                 rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);

  192.                 if (rc != NGX_OK) {
  193.                     return rc;
  194.                 }

  195.                 dir_tested = 1;
  196.             }

  197.             if (of.err == NGX_ENOENT) {
  198.                 continue;
  199.             }

  200.             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
  201.                           "%s \"%s\" failed", of.failed, path.data);

  202.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  203.         }

  204.         uri.len = r->uri.len + len - 1;

  205.         if (!clcf->alias) {
  206.             uri.data = path.data + root;

  207.         } else {
  208.             uri.data = ngx_pnalloc(r->pool, uri.len);
  209.             if (uri.data == NULL) {
  210.                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
  211.             }

  212.             p = ngx_copy(uri.data, r->uri.data, r->uri.len);
  213.             ngx_memcpy(p, name, len - 1);
  214.         }

  215.         return ngx_http_internal_redirect(r, &uri, &r->args);
  216.     }

  217.     return NGX_DECLINED;
  218. }


  219. static ngx_int_t
  220. ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,
  221.     u_char *path, u_char *last)
  222. {
  223.     u_char                c;
  224.     ngx_str_t             dir;
  225.     ngx_open_file_info_t  of;

  226.     c = *last;
  227.     if (c != '/' || path == last) {
  228.         /* "alias" without trailing slash */
  229.         c = *(++last);
  230.     }
  231.     *last = '\0';

  232.     dir.len = last - path;
  233.     dir.data = path;

  234.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  235.                    "http index check dir: \"%V\"", &dir);

  236.     ngx_memzero(&of, sizeof(ngx_open_file_info_t));

  237.     of.test_dir = 1;
  238.     of.test_only = 1;
  239.     of.valid = clcf->open_file_cache_valid;
  240.     of.errors = clcf->open_file_cache_errors;

  241.     if (ngx_http_set_disable_symlinks(r, clcf, &dir, &of) != NGX_OK) {
  242.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  243.     }

  244.     if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)
  245.         != NGX_OK)
  246.     {
  247.         if (of.err) {

  248. #if (NGX_HAVE_OPENAT)
  249.             if (of.err == NGX_EMLINK
  250.                 || of.err == NGX_ELOOP)
  251.             {
  252.                 return NGX_HTTP_FORBIDDEN;
  253.             }
  254. #endif

  255.             if (of.err == NGX_ENOENT) {
  256.                 *last = c;
  257.                 return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT);
  258.             }

  259.             if (of.err == NGX_EACCES) {

  260.                 *last = c;

  261.                 /*
  262.                  * ngx_http_index_test_dir() is called after the first index
  263.                  * file testing has returned an error distinct from NGX_EACCES.
  264.                  * This means that directory searching is allowed.
  265.                  */

  266.                 return NGX_OK;
  267.             }

  268.             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
  269.                           "%s \"%s\" failed", of.failed, dir.data);
  270.         }

  271.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  272.     }

  273.     *last = c;

  274.     if (of.is_dir) {
  275.         return NGX_OK;
  276.     }

  277.     ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
  278.                   "\"%s\" is not a directory", dir.data);

  279.     return NGX_HTTP_INTERNAL_SERVER_ERROR;
  280. }


  281. static ngx_int_t
  282. ngx_http_index_error(ngx_http_request_t *r, ngx_http_core_loc_conf_t  *clcf,
  283.     u_char *file, ngx_err_t err)
  284. {
  285.     if (err == NGX_EACCES) {
  286.         ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
  287.                       "\"%s\" is forbidden", file);

  288.         return NGX_HTTP_FORBIDDEN;
  289.     }

  290.     if (clcf->log_not_found) {
  291.         ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
  292.                       "\"%s\" is not found", file);
  293.     }

  294.     return NGX_HTTP_NOT_FOUND;
  295. }


  296. static void *
  297. ngx_http_index_create_loc_conf(ngx_conf_t *cf)
  298. {
  299.     ngx_http_index_loc_conf_t  *conf;

  300.     conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t));
  301.     if (conf == NULL) {
  302.         return NULL;
  303.     }

  304.     conf->indices = NULL;
  305.     conf->max_index_len = 0;

  306.     return conf;
  307. }


  308. static char *
  309. ngx_http_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  310. {
  311.     ngx_http_index_loc_conf_t  *prev = parent;
  312.     ngx_http_index_loc_conf_t  *conf = child;

  313.     ngx_http_index_t  *index;

  314.     if (conf->indices == NULL) {
  315.         conf->indices = prev->indices;
  316.         conf->max_index_len = prev->max_index_len;
  317.     }

  318.     if (conf->indices == NULL) {
  319.         conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t));
  320.         if (conf->indices == NULL) {
  321.             return NGX_CONF_ERROR;
  322.         }

  323.         index = ngx_array_push(conf->indices);
  324.         if (index == NULL) {
  325.             return NGX_CONF_ERROR;
  326.         }

  327.         index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX);
  328.         index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX;
  329.         index->lengths = NULL;
  330.         index->values = NULL;

  331.         conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX);

  332.         return NGX_CONF_OK;
  333.     }

  334.     return NGX_CONF_OK;
  335. }


  336. static ngx_int_t
  337. ngx_http_index_init(ngx_conf_t *cf)
  338. {
  339.     ngx_http_handler_pt        *h;
  340.     ngx_http_core_main_conf_t  *cmcf;

  341.     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

  342.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
  343.     if (h == NULL) {
  344.         return NGX_ERROR;
  345.     }

  346.     *h = ngx_http_index_handler;

  347.     return NGX_OK;
  348. }


  349. /* TODO: warn about duplicate indices */

  350. static char *
  351. ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  352. {
  353.     ngx_http_index_loc_conf_t *ilcf = conf;

  354.     ngx_str_t                  *value;
  355.     ngx_uint_t                  i, n;
  356.     ngx_http_index_t           *index;
  357.     ngx_http_script_compile_t   sc;

  358.     if (ilcf->indices == NULL) {
  359.         ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t));
  360.         if (ilcf->indices == NULL) {
  361.             return NGX_CONF_ERROR;
  362.         }
  363.     }

  364.     value = cf->args->elts;

  365.     for (i = 1; i < cf->args->nelts; i++) {

  366.         if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {
  367.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  368.                                "only the last index in \"index\" directive "
  369.                                "should be absolute");
  370.         }

  371.         if (value[i].len == 0) {
  372.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  373.                                "index \"%V\" in \"index\" directive is invalid",
  374.                                &value[i]);
  375.             return NGX_CONF_ERROR;
  376.         }

  377.         index = ngx_array_push(ilcf->indices);
  378.         if (index == NULL) {
  379.             return NGX_CONF_ERROR;
  380.         }

  381.         index->name.len = value[i].len;
  382.         index->name.data = value[i].data;
  383.         index->lengths = NULL;
  384.         index->values = NULL;

  385.         n = ngx_http_script_variables_count(&value[i]);

  386.         if (n == 0) {
  387.             if (ilcf->max_index_len < index->name.len) {
  388.                 ilcf->max_index_len = index->name.len;
  389.             }

  390.             if (index->name.data[0] == '/') {
  391.                 continue;
  392.             }

  393.             /* include the terminating '\0' to the length to use ngx_memcpy() */
  394.             index->name.len++;

  395.             continue;
  396.         }

  397.         ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

  398.         sc.cf = cf;
  399.         sc.source = &value[i];
  400.         sc.lengths = &index->lengths;
  401.         sc.values = &index->values;
  402.         sc.variables = n;
  403.         sc.complete_lengths = 1;
  404.         sc.complete_values = 1;

  405.         if (ngx_http_script_compile(&sc) != NGX_OK) {
  406.             return NGX_CONF_ERROR;
  407.         }
  408.     }

  409.     return NGX_CONF_OK;
  410. }