[PATCH v3 07/12] Static: add "index" option

Alejandro Colomar alx.manpages at gmail.com
Sun Dec 19 01:30:25 UTC 2021


This supports a new option "index", that configures a custom index
file name that will be served when a directory is requested.  This
initial support only allows a single fixed string.  An example:

{
	"share": "/www/data/static/$uri",
	"index": "lookatthis.htm"
}

When <example.com/foo/bar/> is requested,
</www/data/static/foo/bar/lookatthis.html> will be served.

If the option is missing, default to "index.html".

===

nxt_conf_validator.c:

Accept "index" as a member of "share", and make sure it is a
string.

===

test_static.py:

Rename <index.html> to <index_.html>, to be able to test this new
option, and set "index": "index_.html".

Perhaps we should also add a test to check that if "index" is
omitted, the default "index.html" is used.

I also tried this feature in my own computer, where I tried the
following:

- Setting "index" to "lookatthis.htm", and check that the correct
  file is being served (check both a different name and a
  different extension).
- Not setting "index", and check that <index.html> is being
  served.
- Settind "index" to an array of strings, and check that the
  configuration fails:

{
	"error": "Invalid configuration.",
	"detail": "The \"index\" value must be a string, but not an array."
}

===

TODO:
- Multiple filenames in the 'index' option.

Signed-off-by: Alejandro Colomar <alx.manpages at gmail.com>
Cc: Nginx Unit <unit at nginx.org>
Cc: "Valentin V. Bartenev" <vbart at nginx.com>
Cc: Zhidao HONG <z.hong at f5.com>
Cc: Igor Sysoev <igor at sysoev.ru>
Cc: Oisin Canty <o.canty at f5.com>
---
 docs/changes.xml          |  6 ++++++
 src/nxt_conf_validation.c |  3 +++
 src/nxt_http.h            |  1 +
 src/nxt_http_route.c      |  5 +++++
 src/nxt_http_static.c     | 32 +++++++++++++++++++++++---------
 test/test_static.py       | 13 +++++++------
 6 files changed, 45 insertions(+), 15 deletions(-)

diff --git a/docs/changes.xml b/docs/changes.xml
index 8890e4d..07f3c83 100644
--- a/docs/changes.xml
+++ b/docs/changes.xml
@@ -18,6 +18,12 @@
          date="" time=""
          packager="Andrei Belov <defan at nginx.com>">
 
+<change type="feature">
+<para>
+new "index" option to specify a custom index file name.
+</para>
+</change>
+
 <change>
 <para>
 NGINX Unit updated to 1.27.0.
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index 7edd638..bdc38b0 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -645,6 +645,9 @@ static nxt_conf_vldt_object_t  nxt_conf_vldt_share_action_members[] = {
         .name       = nxt_string("share"),
         .type       = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
         .validator  = nxt_conf_vldt_share,
+    }, {
+        .name       = nxt_string("index"),
+        .type       = NXT_CONF_VLDT_STRING,
     }, {
         .name       = nxt_string("types"),
         .type       = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
diff --git a/src/nxt_http.h b/src/nxt_http.h
index 7cef134..9b819fb 100644
--- a/src/nxt_http.h
+++ b/src/nxt_http.h
@@ -209,6 +209,7 @@ typedef struct {
     nxt_str_t                       location;
     nxt_conf_value_t                *proxy;
     nxt_conf_value_t                *share;
+    nxt_conf_value_t                *index;
     nxt_str_t                       chroot;
     nxt_conf_value_t                *follow_symlinks;
     nxt_conf_value_t                *traverse_mounts;
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index 26f0662..c92f2cd 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -639,6 +639,11 @@ static nxt_conf_map_t  nxt_http_route_action_conf[] = {
         NXT_CONF_MAP_PTR,
         offsetof(nxt_http_action_conf_t, share)
     },
+    {
+        nxt_string("index"),
+        NXT_CONF_MAP_PTR,
+        offsetof(nxt_http_action_conf_t, index)
+    },
     {
         nxt_string("chroot"),
         NXT_CONF_MAP_STR,
diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c
index 663f896..b443a54 100644
--- a/src/nxt_http_static.c
+++ b/src/nxt_http_static.c
@@ -19,6 +19,7 @@ typedef struct {
 typedef struct {
     nxt_uint_t                  nshares;
     nxt_http_static_share_t     *shares;
+    nxt_str_t                   index;
 #if (NXT_HAVE_OPENAT2)
     nxt_var_t                   *chroot;
     nxt_uint_t                  resolve;
@@ -30,6 +31,7 @@ typedef struct {
 typedef struct {
     nxt_http_action_t           *action;
     nxt_str_t                   share;
+    nxt_str_t                   index;
 #if (NXT_HAVE_OPENAT2)
     nxt_str_t                   chroot;
 #endif
@@ -80,6 +82,8 @@ nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
     nxt_conf_value_t        *cv;
     nxt_http_static_conf_t  *conf;
 
+    static const nxt_str_t  default_index = nxt_string("index.html");
+
     mp = tmcf->router_conf->mem_pool;
 
     conf = nxt_mp_zget(mp, sizeof(nxt_http_static_conf_t));
@@ -110,6 +114,13 @@ nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
         conf->shares[i].is_const = nxt_var_is_const(var);
     }
 
+    if (acf->index == NULL) {
+        conf->index = default_index;
+
+    } else {
+        nxt_conf_get_string(acf->index, &conf->index);
+    }
+
 #if (NXT_HAVE_OPENAT2)
     if (acf->chroot.length > 0) {
         nxt_str_t   chr, shr;
@@ -222,8 +233,10 @@ nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r,
 
 #if (NXT_DEBUG)
     nxt_str_t  shr;
+    nxt_str_t  idx;
 
     nxt_var_raw(share->var, &shr);
+    idx = conf->index;
 
 #if (NXT_HAVE_OPENAT2)
     nxt_str_t  chr;
@@ -235,12 +248,15 @@ nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r,
         nxt_str_set(&chr, "");
     }
 
-    nxt_debug(task, "http static: \"%V\" (chroot: \"%V\")", &shr, &chr);
+    nxt_debug(task, "http static: \"%V\", index: \"%V\" (chroot: \"%V\")",
+        &shr, &idx, &chr);
 #else
-    nxt_debug(task, "http static: \"%V\"", &shr);
+    nxt_debug(task, "http static: \"%V\", index: \"%V\"", &shr, &idx);
 #endif
 #endif /* NXT_DEBUG */
 
+    ctx->index = conf->index;
+
     if (share->is_const) {
         nxt_var_raw(share->var, &ctx->share);
 
@@ -282,7 +298,7 @@ nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data)
     struct tm               tm;
     nxt_buf_t               *fb;
     nxt_int_t               ret;
-    nxt_str_t               *shr, exten, *mtype;
+    nxt_str_t               *shr, *index, exten, *mtype;
     nxt_uint_t              level;
     nxt_file_t              *f, file;
     nxt_file_info_t         fi;
@@ -295,8 +311,6 @@ nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data)
     nxt_http_static_ctx_t   *ctx;
     nxt_http_static_conf_t  *conf;
 
-    static const nxt_str_t  index = nxt_string("index.html");
-
     r = obj;
     ctx = data;
     action = ctx->action;
@@ -307,12 +321,12 @@ nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data)
     mtype = NULL;
 
     shr = &ctx->share;
+    index = &ctx->index;
 
     if (shr->start[shr->length - 1] == '/') {
-        /* TODO: dynamic index setting. */
-        nxt_str_set(&exten, ".html");
+        nxt_http_static_extract_extension(index, &exten);
 
-        length = shr->length + index.length;
+        length = shr->length + index->length;
 
         fname = nxt_mp_nget(r->mem_pool, length + 1);
         if (nxt_slow_path(fname == NULL)) {
@@ -321,7 +335,7 @@ nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data)
 
         p = fname;
         p = nxt_cpymem(p, shr->start, shr->length);
-        p = nxt_cpymem(p, index.start, index.length);
+        p = nxt_cpymem(p, index->start, index->length);
         *p = '\0';
 
     } else {
diff --git a/test/test_static.py b/test/test_static.py
index 76668a1..70ae676 100644
--- a/test/test_static.py
+++ b/test/test_static.py
@@ -14,7 +14,7 @@ class TestStatic(TestApplicationProto):
 
     def setup_method(self):
         os.makedirs(option.temp_dir + '/assets/dir')
-        with open(option.temp_dir + '/assets/index.html', 'w') as index, open(
+        with open(option.temp_dir + '/assets/index_.html', 'w') as index, open(
             option.temp_dir + '/assets/README', 'w'
         ) as readme, open(
             option.temp_dir + '/assets/log.log', 'w'
@@ -32,7 +32,8 @@ class TestStatic(TestApplicationProto):
                 "routes": [
                     {
                         "action": {
-                            "share": option.temp_dir + "/assets$uri"
+                            "share": option.temp_dir + "/assets$uri",
+                            "index": "index_.html"
                         }
                     }
                 ],
@@ -90,7 +91,7 @@ class TestStatic(TestApplicationProto):
         assert self.get(url='/')['body'] == '0123456789', 'before 1.26.0 2'
 
     def test_static_index(self):
-        assert self.get(url='/index.html')['body'] == '0123456789', 'index'
+        assert self.get(url='/index_.html')['body'] == '0123456789', 'index'
         assert self.get(url='/')['body'] == '0123456789', 'index 2'
         assert self.get(url='//')['body'] == '0123456789', 'index 3'
         assert self.get(url='/.')['body'] == '0123456789', 'index 4'
@@ -99,7 +100,7 @@ class TestStatic(TestApplicationProto):
         assert self.get(url='/#blah')['body'] == '0123456789', 'index anchor'
         assert self.get(url='/dir/')['status'] == 404, 'index not found'
 
-        resp = self.get(url='/index.html/')
+        resp = self.get(url='/index_.html/')
         assert resp['status'] == 404, 'index not found 2 status'
         assert (
             resp['headers']['Content-Type'] == 'text/html'
@@ -123,7 +124,7 @@ class TestStatic(TestApplicationProto):
         assert etag != etag_2, 'different ETag'
         assert etag == self.get(url='/')['headers']['ETag'], 'same ETag'
 
-        with open(temp_dir + '/assets/index.html', 'w') as f:
+        with open(temp_dir + '/assets/index_.html', 'w') as f:
             f.write('blah')
 
         assert etag != self.get(url='/')['headers']['ETag'], 'new ETag'
@@ -262,7 +263,7 @@ class TestStatic(TestApplicationProto):
             == 'text/x-code/x-blah/x-blah'
         ), 'mime_types string case insensitive'
         assert (
-            self.get(url='/index.html')['headers']['Content-Type']
+            self.get(url='/index_.html')['headers']['Content-Type']
             == 'text/plain'
         ), 'mime_types html'
         assert (
-- 
2.34.1



More information about the unit mailing list