src/http/modules/ngx_http_index_module.c - nginx-1.7.10

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.     index = ilcf->indices->elts;
  100.     for (i = 0; i < ilcf->indices->nelts; i++) {

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

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

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

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

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

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

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

  118.             /* 16 bytes are preallocation */

  119.             reserve = len + 16;
  120.         }

  121.         if (reserve > allocated) {

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

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

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

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

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

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

  132.         } else {
  133.             e.ip = index[i].values->elts;
  134.             e.pos = name;

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

  139.             if (*name == '/') {
  140.                 uri.len = len - 1;
  141.                 uri.data = name;
  142.                 return ngx_http_internal_redirect(r, &uri, &r->args);
  143.             }

  144.             path.len = e.pos - path.data;

  145.             *e.pos = '\0';
  146.         }

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

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

  150.         of.read_ahead = clcf->read_ahead;
  151.         of.directio = clcf->directio;
  152.         of.valid = clcf->open_file_cache_valid;
  153.         of.min_uses = clcf->open_file_cache_min_uses;
  154.         of.test_only = 1;
  155.         of.errors = clcf->open_file_cache_errors;
  156.         of.events = clcf->open_file_cache_events;

  157.         if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
  158.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  159.         }

  160.         if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
  161.             != NGX_OK)
  162.         {
  163.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
  164.                            "%s \"%s\" failed", of.failed, path.data);

  165.             if (of.err == 0) {
  166.                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
  167.             }

  168. #if (NGX_HAVE_OPENAT)
  169.             if (of.err == NGX_EMLINK
  170.                 || of.err == NGX_ELOOP)
  171.             {
  172.                 return NGX_HTTP_FORBIDDEN;
  173.             }
  174. #endif

  175.             if (of.err == NGX_ENOTDIR
  176.                 || of.err == NGX_ENAMETOOLONG
  177.                 || of.err == NGX_EACCES)
  178.             {
  179.                 return ngx_http_index_error(r, clcf, path.data, of.err);
  180.             }

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

  183.                 if (rc != NGX_OK) {
  184.                     return rc;
  185.                 }

  186.                 dir_tested = 1;
  187.             }

  188.             if (of.err == NGX_ENOENT) {
  189.                 continue;
  190.             }

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

  193.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  194.         }

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

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

  198.         } else {
  199.             uri.data = ngx_pnalloc(r->pool, uri.len);
  200.             if (uri.data == NULL) {
  201.                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
  202.             }

  203.             p = ngx_copy(uri.data, r->uri.data, r->uri.len);
  204.             ngx_memcpy(p, name, len - 1);
  205.         }

  206.         return ngx_http_internal_redirect(r, &uri, &r->args);
  207.     }

  208.     return NGX_DECLINED;
  209. }


  210. static ngx_int_t
  211. ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,
  212.     u_char *path, u_char *last)
  213. {
  214.     u_char                c;
  215.     ngx_str_t             dir;
  216.     ngx_open_file_info_t  of;

  217.     c = *last;
  218.     if (c != '/' || path == last) {
  219.         /* "alias" without trailing slash */
  220.         c = *(++last);
  221.     }
  222.     *last = '\0';

  223.     dir.len = last - path;
  224.     dir.data = path;

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

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

  228.     of.test_dir = 1;
  229.     of.test_only = 1;
  230.     of.valid = clcf->open_file_cache_valid;
  231.     of.errors = clcf->open_file_cache_errors;

  232.     if (ngx_http_set_disable_symlinks(r, clcf, &dir, &of) != NGX_OK) {
  233.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  234.     }

  235.     if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)
  236.         != NGX_OK)
  237.     {
  238.         if (of.err) {

  239. #if (NGX_HAVE_OPENAT)
  240.             if (of.err == NGX_EMLINK
  241.                 || of.err == NGX_ELOOP)
  242.             {
  243.                 return NGX_HTTP_FORBIDDEN;
  244.             }
  245. #endif

  246.             if (of.err == NGX_ENOENT) {
  247.                 *last = c;
  248.                 return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT);
  249.             }

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

  251.                 *last = c;

  252.                 /*
  253.                  * ngx_http_index_test_dir() is called after the first index
  254.                  * file testing has returned an error distinct from NGX_EACCES.
  255.                  * This means that directory searching is allowed.
  256.                  */

  257.                 return NGX_OK;
  258.             }

  259.             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
  260.                           "%s \"%s\" failed", of.failed, dir.data);
  261.         }

  262.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  263.     }

  264.     *last = c;

  265.     if (of.is_dir) {
  266.         return NGX_OK;
  267.     }

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

  270.     return NGX_HTTP_INTERNAL_SERVER_ERROR;
  271. }


  272. static ngx_int_t
  273. ngx_http_index_error(ngx_http_request_t *r, ngx_http_core_loc_conf_t  *clcf,
  274.     u_char *file, ngx_err_t err)
  275. {
  276.     if (err == NGX_EACCES) {
  277.         ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
  278.                       "\"%s\" is forbidden", file);

  279.         return NGX_HTTP_FORBIDDEN;
  280.     }

  281.     if (clcf->log_not_found) {
  282.         ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
  283.                       "\"%s\" is not found", file);
  284.     }

  285.     return NGX_HTTP_NOT_FOUND;
  286. }


  287. static void *
  288. ngx_http_index_create_loc_conf(ngx_conf_t *cf)
  289. {
  290.     ngx_http_index_loc_conf_t  *conf;

  291.     conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t));
  292.     if (conf == NULL) {
  293.         return NULL;
  294.     }

  295.     conf->indices = NULL;
  296.     conf->max_index_len = 0;

  297.     return conf;
  298. }


  299. static char *
  300. ngx_http_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  301. {
  302.     ngx_http_index_loc_conf_t  *prev = parent;
  303.     ngx_http_index_loc_conf_t  *conf = child;

  304.     ngx_http_index_t  *index;

  305.     if (conf->indices == NULL) {
  306.         conf->indices = prev->indices;
  307.         conf->max_index_len = prev->max_index_len;
  308.     }

  309.     if (conf->indices == NULL) {
  310.         conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t));
  311.         if (conf->indices == NULL) {
  312.             return NGX_CONF_ERROR;
  313.         }

  314.         index = ngx_array_push(conf->indices);
  315.         if (index == NULL) {
  316.             return NGX_CONF_ERROR;
  317.         }

  318.         index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX);
  319.         index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX;
  320.         index->lengths = NULL;
  321.         index->values = NULL;

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

  323.         return NGX_CONF_OK;
  324.     }

  325.     return NGX_CONF_OK;
  326. }


  327. static ngx_int_t
  328. ngx_http_index_init(ngx_conf_t *cf)
  329. {
  330.     ngx_http_handler_pt        *h;
  331.     ngx_http_core_main_conf_t  *cmcf;

  332.     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

  333.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
  334.     if (h == NULL) {
  335.         return NGX_ERROR;
  336.     }

  337.     *h = ngx_http_index_handler;

  338.     return NGX_OK;
  339. }


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

  341. static char *
  342. ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  343. {
  344.     ngx_http_index_loc_conf_t *ilcf = conf;

  345.     ngx_str_t                  *value;
  346.     ngx_uint_t                  i, n;
  347.     ngx_http_index_t           *index;
  348.     ngx_http_script_compile_t   sc;

  349.     if (ilcf->indices == NULL) {
  350.         ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t));
  351.         if (ilcf->indices == NULL) {
  352.             return NGX_CONF_ERROR;
  353.         }
  354.     }

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

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

  357.         if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {
  358.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  359.                                "only the last index in \"index\" directive "
  360.                                "should be absolute");
  361.         }

  362.         if (value[i].len == 0) {
  363.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  364.                                "index \"%V\" in \"index\" directive is invalid",
  365.                                &value[1]);
  366.             return NGX_CONF_ERROR;
  367.         }

  368.         index = ngx_array_push(ilcf->indices);
  369.         if (index == NULL) {
  370.             return NGX_CONF_ERROR;
  371.         }

  372.         index->name.len = value[i].len;
  373.         index->name.data = value[i].data;
  374.         index->lengths = NULL;
  375.         index->values = NULL;

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

  377.         if (n == 0) {
  378.             if (ilcf->max_index_len < index->name.len) {
  379.                 ilcf->max_index_len = index->name.len;
  380.             }

  381.             if (index->name.data[0] == '/') {
  382.                 continue;
  383.             }

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

  386.             continue;
  387.         }

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

  389.         sc.cf = cf;
  390.         sc.source = &value[i];
  391.         sc.lengths = &index->lengths;
  392.         sc.values = &index->values;
  393.         sc.variables = n;
  394.         sc.complete_lengths = 1;
  395.         sc.complete_values = 1;

  396.         if (ngx_http_script_compile(&sc) != NGX_OK) {
  397.             return NGX_CONF_ERROR;
  398.         }
  399.     }

  400.     return NGX_CONF_OK;
  401. }