[RFC] [PATCH] Autoindex: support sorting using URL parameters

Peter Wu lekensteyn at gmail.com
Tue Jan 29 17:49:46 UTC 2013


Based on Apache HTTPD autoindex docs[1]. Supported:
- C=N sorts the directory by file name
- C=M sorts the directory by last-modified date, then file name
- C=S sorts the directory by size, then file name
- O=A sorts the listing in Ascending Order
- O=D sorts the listing in Descending Order

Not supported (does not make sense for nginx):
- C=D sorts the directory by description, then file name
- All F= (FancyIndex) related arguments
- Version sorting for file names (V=)
- Pattern filter (P=)

Argument processing stops when the query string does not exactly match the
options allowed by nginx, invalid values (like "C=m", "C=x" or "C=foo") are
ignored and cause further processing to stop.

C and O are the most useful options and can commonly be found in my old Apache
logs (also outputted in header links). This patch is made for these cases, not
for some exotic use of Apache-specific properties.

 [1]: http://httpd.apache.org/docs/2.4/mod/mod_autoindex.html
---
(this patch is also available at http://lekensteyn.nl/files/nginx/)

RFC: this patch only adds support for URL parameters. Before implementing the 
actual clickable headers I would like to have some feedback. Do you actually
want to add such a sorting feature to the standard autoindex module? Sorting
has also been requested on http://forum.nginx.org/read.php?10,211728

I am aware of FancyIndex[2] (which does not support sorting either), but having
two separate modules to do the exact same thing seems a bit useless.

About the implementation, I considered using the non-standard qsort_r
(different prototypes on BSD and Linux, qsort_s on Windows) for adding the sort
options. Because these prototypes and their compare functions are incompatible
I dropped that idea. Next was augmenting the directory entries struct to make
the options available, this is currently the way how it is implemented.

Parsing URL parameters is done in a separate function which is currently
non-void. Since the return value is not used at the moment, it can also be make
void. Thoughts?

 [2]: http://wiki.nginx.org/NgxFancyIndex
---
 src/http/modules/ngx_http_autoindex_module.c | 93 +++++++++++++++++++++++++++-
 1 file changed, 92 insertions(+), 1 deletion(-)

diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c
index 450a48e..aa1be25 100644
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -24,6 +24,16 @@ typedef struct {
 
 
 typedef struct {
+    u_char         sort_key;
+    unsigned       order_desc:1;
+} ngx_http_autoindex_opts_t;
+
+#define NGX_AUTOINDEX_SORT_NAME     'N'
+#define NGX_AUTOINDEX_SORT_MTIME    'M'
+#define NGX_AUTOINDEX_SORT_SIZE     'S'
+
+
+typedef struct {
     ngx_str_t      name;
     size_t         utf_len;
     size_t         escape;
@@ -33,6 +43,8 @@ typedef struct {
 
     time_t         mtime;
     off_t          size;
+
+    ngx_http_autoindex_opts_t *sort_opts;
 } ngx_http_autoindex_entry_t;
 
 
@@ -50,6 +62,8 @@ typedef struct {
 
 static int ngx_libc_cdecl ngx_http_autoindex_cmp_entries(const void *one,
     const void *two);
+static ngx_int_t ngx_http_autoindex_get_opts(ngx_http_request_t *r,
+    ngx_http_autoindex_opts_t *opts);
 static ngx_int_t ngx_http_autoindex_error(ngx_http_request_t *r,
     ngx_dir_t *dir, ngx_str_t *name);
 static ngx_int_t ngx_http_autoindex_init(ngx_conf_t *cf);
@@ -153,6 +167,7 @@ ngx_http_autoindex_handler(ngx_http_request_t *r)
     ngx_array_t                     entries;
     ngx_http_autoindex_entry_t     *entry;
     ngx_http_autoindex_loc_conf_t  *alcf;
+    ngx_http_autoindex_opts_t       opts;
 
     static char  *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
@@ -214,6 +229,8 @@ ngx_http_autoindex_handler(ngx_http_request_t *r)
         return rc;
     }
 
+    ngx_http_autoindex_get_opts(r, &opts);
+
 #if (NGX_SUPPRESS_WARN)
 
     /* MSVC thinks 'entries' may be used without having been initialized */
@@ -353,6 +370,8 @@ ngx_http_autoindex_handler(ngx_http_request_t *r)
         entry->dir = ngx_de_is_dir(&dir);
         entry->mtime = ngx_de_mtime(&dir);
         entry->size = ngx_de_size(&dir);
+
+        entry->sort_opts = &opts;
     }
 
     if (ngx_close_dir(&dir) == NGX_ERROR) {
@@ -580,11 +599,67 @@ ngx_http_autoindex_handler(ngx_http_request_t *r)
 }
 
 
+static ngx_int_t
+ngx_http_autoindex_get_opts(ngx_http_request_t *r,
+    ngx_http_autoindex_opts_t *opts)
+{
+    u_char *p, *last, key, val;
+
+    opts->sort_key = NGX_AUTOINDEX_SORT_NAME;
+    opts->order_desc = 0;
+
+    p = r->args.data;
+    last = p + r->args.len;
+
+    for ( /* void */; p < last; p++) {
+
+        key = *p++;
+
+        if (*p++ != '=') {
+            return NGX_DECLINED;
+        }
+
+        val = *p++;
+
+        /* assume one-letter value and expect separator */
+        if (p < last && *p != '&' && *p != ';') {
+            return NGX_DECLINED;
+        }
+
+        if (key == 'C') {
+            switch (val) {
+            case NGX_AUTOINDEX_SORT_NAME:
+            case NGX_AUTOINDEX_SORT_MTIME:
+            case NGX_AUTOINDEX_SORT_SIZE:
+                opts->sort_key = val;
+                break;
+            default:
+                return NGX_DECLINED;
+            }
+        } else if (key == 'O') {
+            if (val == 'D') {
+                opts->order_desc = 1;
+            } else if (val == 'A') {
+                opts->order_desc = 0;
+            } else {
+                return NGX_DECLINED;
+            }
+        } else {
+            return NGX_DECLINED;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
 static int ngx_libc_cdecl
 ngx_http_autoindex_cmp_entries(const void *one, const void *two)
 {
     ngx_http_autoindex_entry_t *first = (ngx_http_autoindex_entry_t *) one;
     ngx_http_autoindex_entry_t *second = (ngx_http_autoindex_entry_t *) two;
+    ngx_http_autoindex_opts_t *opts = first->sort_opts;
+    int r;
 
     if (first->dir && !second->dir) {
         /* move the directories to the start */
@@ -596,7 +671,23 @@ ngx_http_autoindex_cmp_entries(const void *one, const void *two)
         return 1;
     }
 
-    return (int) ngx_strcmp(first->name.data, second->name.data);
+    switch (opts->sort_key) {
+    case NGX_AUTOINDEX_SORT_MTIME:
+        r = first->mtime - second->mtime;
+        break;
+    case NGX_AUTOINDEX_SORT_SIZE:
+        r = first->size - second->size;
+        break;
+    default: /* includes NGX_AUTOINDEX_SORT_NAME */
+        r = 0;
+        break;
+    }
+
+    if (r == 0) {
+        r = ngx_strcmp(first->name.data, second->name.data);
+    }
+
+    return opts->order_desc ? -r : r;
 }
 
 
-- 
1.8.1.1




More information about the nginx-devel mailing list