[nginx] Autoindex: implemented JSON output format.

Valentin Bartenev vbart at nginx.com
Fri Dec 12 17:26:55 UTC 2014


details:   http://hg.nginx.org/nginx/rev/33c08d7e2915
branches:  
changeset: 5944:33c08d7e2915
user:      Valentin Bartenev <vbart at nginx.com>
date:      Fri Dec 12 20:25:35 2014 +0300
description:
Autoindex: implemented JSON output format.

diffstat:

 src/core/ngx_string.c                        |   52 ++++++
 src/core/ngx_string.h                        |    1 +
 src/http/modules/ngx_http_autoindex_module.c |  226 ++++++++++++++++++++++++++-
 3 files changed, 274 insertions(+), 5 deletions(-)

diffs (truncated from 402 to 300 lines):

diff -r 631dee7bfd4e -r 33c08d7e2915 src/core/ngx_string.c
--- a/src/core/ngx_string.c	Fri Dec 12 20:25:28 2014 +0300
+++ b/src/core/ngx_string.c	Fri Dec 12 20:25:35 2014 +0300
@@ -1773,6 +1773,58 @@ ngx_escape_html(u_char *dst, u_char *src
 }
 
 
+uintptr_t
+ngx_escape_json(u_char *dst, u_char *src, size_t size)
+{
+    u_char      ch;
+    ngx_uint_t  len;
+
+    if (dst == NULL) {
+        len = 0;
+
+        while (size) {
+            ch = *src++;
+
+            if (ch == '\\' || ch == '"') {
+                len++;
+
+            } else if (ch <= 0x1f) {
+                len += sizeof("\\u001F") - 2;
+            }
+
+            size--;
+        }
+
+        return (uintptr_t) len;
+    }
+
+    while (size) {
+        ch = *src++;
+
+        if (ch > 0x1f) {
+
+            if (ch == '\\' || ch == '"') {
+                *dst++ = '\\';
+            }
+
+            *dst++ = ch;
+
+        } else {
+            *dst++ = '\\'; *dst++ = 'u'; *dst++ = '0'; *dst++ = '0';
+            *dst++ = '0' + (ch >> 4);
+
+            ch &= 0xf;
+
+            *dst++ = (ch < 10) ? ('0' + ch) : ('A' + ch - 10);
+        }
+
+        size--;
+    }
+
+    return (uintptr_t) dst;
+}
+
+
 void
 ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,
     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
diff -r 631dee7bfd4e -r 33c08d7e2915 src/core/ngx_string.h
--- a/src/core/ngx_string.h	Fri Dec 12 20:25:28 2014 +0300
+++ b/src/core/ngx_string.h	Fri Dec 12 20:25:35 2014 +0300
@@ -207,6 +207,7 @@ uintptr_t ngx_escape_uri(u_char *dst, u_
     ngx_uint_t type);
 void ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type);
 uintptr_t ngx_escape_html(u_char *dst, u_char *src, size_t size);
+uintptr_t ngx_escape_json(u_char *dst, u_char *src, size_t size);
 
 
 typedef struct {
diff -r 631dee7bfd4e -r 33c08d7e2915 src/http/modules/ngx_http_autoindex_module.c
--- a/src/http/modules/ngx_http_autoindex_module.c	Fri Dec 12 20:25:28 2014 +0300
+++ b/src/http/modules/ngx_http_autoindex_module.c	Fri Dec 12 20:25:35 2014 +0300
@@ -30,6 +30,7 @@ typedef struct {
     size_t         escape_html;
 
     unsigned       dir:1;
+    unsigned       file:1;
 
     time_t         mtime;
     off_t          size;
@@ -38,11 +39,16 @@ typedef struct {
 
 typedef struct {
     ngx_flag_t     enable;
+    ngx_uint_t     format;
     ngx_flag_t     localtime;
     ngx_flag_t     exact_size;
 } ngx_http_autoindex_loc_conf_t;
 
 
+#define NGX_HTTP_AUTOINDEX_HTML         0
+#define NGX_HTTP_AUTOINDEX_JSON         1
+#define NGX_HTTP_AUTOINDEX_JSONP        2
+
 #define NGX_HTTP_AUTOINDEX_PREALLOCATE  50
 
 #define NGX_HTTP_AUTOINDEX_NAME_LEN     50
@@ -50,16 +56,30 @@ typedef struct {
 
 static ngx_buf_t *ngx_http_autoindex_html(ngx_http_request_t *r,
     ngx_array_t *entries);
+static ngx_buf_t *ngx_http_autoindex_json(ngx_http_request_t *r,
+    ngx_array_t *entries, ngx_str_t *callback);
+static ngx_int_t ngx_http_autoindex_jsonp_callback(ngx_http_request_t *r,
+    ngx_str_t *callback);
+
 static int ngx_libc_cdecl ngx_http_autoindex_cmp_entries(const void *one,
     const void *two);
 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);
 static void *ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf);
 static char *ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf,
     void *parent, void *child);
 
 
+static ngx_conf_enum_t  ngx_http_autoindex_format[] = {
+    { ngx_string("html"), NGX_HTTP_AUTOINDEX_HTML },
+    { ngx_string("json"), NGX_HTTP_AUTOINDEX_JSON },
+    { ngx_string("jsonp"), NGX_HTTP_AUTOINDEX_JSONP },
+    { ngx_null_string, 0 }
+};
+
+
 static ngx_command_t  ngx_http_autoindex_commands[] = {
 
     { ngx_string("autoindex"),
@@ -69,6 +89,13 @@ static ngx_command_t  ngx_http_autoindex
       offsetof(ngx_http_autoindex_loc_conf_t, enable),
       NULL },
 
+    { ngx_string("autoindex_format"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_autoindex_loc_conf_t, format),
+      &ngx_http_autoindex_format },
+
     { ngx_string("autoindex_localtime"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -126,9 +153,9 @@ ngx_http_autoindex_handler(ngx_http_requ
     ngx_err_t                       err;
     ngx_buf_t                      *b;
     ngx_int_t                       rc;
-    ngx_str_t                       path;
+    ngx_str_t                       path, callback;
     ngx_dir_t                       dir;
-    ngx_uint_t                      level;
+    ngx_uint_t                      level, format;
     ngx_pool_t                     *pool;
     ngx_chain_t                     out;
     ngx_array_t                     entries;
@@ -167,6 +194,18 @@ ngx_http_autoindex_handler(ngx_http_requ
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http autoindex: \"%s\"", path.data);
 
+    format = alcf->format;
+
+    if (format == NGX_HTTP_AUTOINDEX_JSONP) {
+        if (ngx_http_autoindex_jsonp_callback(r, &callback) != NGX_OK) {
+            return NGX_HTTP_BAD_REQUEST;
+        }
+
+        if (callback.len == 0) {
+            format = NGX_HTTP_AUTOINDEX_JSON;
+        }
+    }
+
     if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
         err = ngx_errno;
 
@@ -209,8 +248,23 @@ ngx_http_autoindex_handler(ngx_http_requ
     }
 
     r->headers_out.status = NGX_HTTP_OK;
-    r->headers_out.content_type_len = sizeof("text/html") - 1;
-    ngx_str_set(&r->headers_out.content_type, "text/html");
+
+    switch (format) {
+
+    case NGX_HTTP_AUTOINDEX_JSON:
+        ngx_str_set(&r->headers_out.content_type, "application/json");
+        break;
+
+    case NGX_HTTP_AUTOINDEX_JSONP:
+        ngx_str_set(&r->headers_out.content_type, "application/javascript");
+        break;
+
+    default: /* NGX_HTTP_AUTOINDEX_HTML */
+        ngx_str_set(&r->headers_out.content_type, "text/html");
+        break;
+    }
+
+    r->headers_out.content_type_len = r->headers_out.content_type.len;
     r->headers_out.content_type_lowcase = NULL;
 
     rc = ngx_http_send_header(r);
@@ -308,6 +362,7 @@ ngx_http_autoindex_handler(ngx_http_requ
         ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1);
 
         entry->dir = ngx_de_is_dir(&dir);
+        entry->file = ngx_de_is_file(&dir);
         entry->mtime = ngx_de_mtime(&dir);
         entry->size = ngx_de_size(&dir);
     }
@@ -323,7 +378,20 @@ ngx_http_autoindex_handler(ngx_http_requ
                   ngx_http_autoindex_cmp_entries);
     }
 
-    b = ngx_http_autoindex_html(r, &entries);
+    switch (format) {
+
+    case NGX_HTTP_AUTOINDEX_JSON:
+        b = ngx_http_autoindex_json(r, &entries, NULL);
+        break;
+
+    case NGX_HTTP_AUTOINDEX_JSONP:
+        b = ngx_http_autoindex_json(r, &entries, &callback);
+        break;
+
+    default: /* NGX_HTTP_AUTOINDEX_HTML */
+        b = ngx_http_autoindex_html(r, &entries);
+        break;
+    }
 
     if (b == NULL) {
         return NGX_ERROR;
@@ -608,6 +676,151 @@ ngx_http_autoindex_html(ngx_http_request
 }
 
 
+static ngx_buf_t *
+ngx_http_autoindex_json(ngx_http_request_t *r, ngx_array_t *entries,
+    ngx_str_t *callback)
+{
+    size_t                       len;
+    ngx_buf_t                   *b;
+    ngx_uint_t                   i;
+    ngx_http_autoindex_entry_t  *entry;
+
+    len = sizeof("[" CRLF CRLF "]") - 1;
+
+    if (callback) {
+        len += sizeof("/* callback */" CRLF "();") - 1 + callback->len;
+    }
+
+    entry = entries->elts;
+
+    for (i = 0; i < entries->nelts; i++) {
+        entry[i].escape = ngx_escape_json(NULL, entry[i].name.data,
+                                          entry[i].name.len);
+
+        len += sizeof("{  }," CRLF) - 1
+            + sizeof("\"name\":\"\"") - 1
+            + entry[i].name.len + entry[i].escape
+            + sizeof(", \"type\":\"directory\"") - 1
+            + sizeof(", \"mtime\":\"Wed, 31 Dec 1986 10:00:00 GMT\"") - 1;
+
+        if (entry[i].file) {
+            len += sizeof(", \"size\":") - 1 + NGX_OFF_T_LEN;
+        }
+    }
+
+    b = ngx_create_temp_buf(r->pool, len);
+    if (b == NULL) {
+        return NULL;
+    }
+
+    if (callback) {
+        b->last = ngx_cpymem(b->last, "/* callback */" CRLF,
+                             sizeof("/* callback */" CRLF) - 1);
+
+        b->last = ngx_cpymem(b->last, callback->data, callback->len);
+
+        *b->last++ = '(';
+    }
+
+    *b->last++ = '[';
+
+    for (i = 0; i < entries->nelts; i++) {
+        b->last = ngx_cpymem(b->last, CRLF "{ \"name\":\"",
+                             sizeof(CRLF "{ \"name\":\"") - 1);
+
+        if (entry[i].escape) {
+            b->last = (u_char *) ngx_escape_json(b->last, entry[i].name.data,
+                                                 entry[i].name.len);
+        } else {
+            b->last = ngx_cpymem(b->last, entry[i].name.data,
+                                 entry[i].name.len);
+        }
+
+        b->last = ngx_cpymem(b->last, "\", \"type\":\"",
+                             sizeof("\", \"type\":\"") - 1);
+



More information about the nginx-devel mailing list