Mercurial > hg > nginx-quic
comparison src/http/modules/ngx_http_autoindex_module.c @ 5944:33c08d7e2915
Autoindex: implemented JSON output format.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Fri, 12 Dec 2014 20:25:35 +0300 |
parents | 631dee7bfd4e |
children | 99751fe3bc3b |
comparison
equal
deleted
inserted
replaced
5943:631dee7bfd4e | 5944:33c08d7e2915 |
---|---|
28 size_t utf_len; | 28 size_t utf_len; |
29 size_t escape; | 29 size_t escape; |
30 size_t escape_html; | 30 size_t escape_html; |
31 | 31 |
32 unsigned dir:1; | 32 unsigned dir:1; |
33 unsigned file:1; | |
33 | 34 |
34 time_t mtime; | 35 time_t mtime; |
35 off_t size; | 36 off_t size; |
36 } ngx_http_autoindex_entry_t; | 37 } ngx_http_autoindex_entry_t; |
37 | 38 |
38 | 39 |
39 typedef struct { | 40 typedef struct { |
40 ngx_flag_t enable; | 41 ngx_flag_t enable; |
42 ngx_uint_t format; | |
41 ngx_flag_t localtime; | 43 ngx_flag_t localtime; |
42 ngx_flag_t exact_size; | 44 ngx_flag_t exact_size; |
43 } ngx_http_autoindex_loc_conf_t; | 45 } ngx_http_autoindex_loc_conf_t; |
44 | 46 |
45 | 47 |
48 #define NGX_HTTP_AUTOINDEX_HTML 0 | |
49 #define NGX_HTTP_AUTOINDEX_JSON 1 | |
50 #define NGX_HTTP_AUTOINDEX_JSONP 2 | |
51 | |
46 #define NGX_HTTP_AUTOINDEX_PREALLOCATE 50 | 52 #define NGX_HTTP_AUTOINDEX_PREALLOCATE 50 |
47 | 53 |
48 #define NGX_HTTP_AUTOINDEX_NAME_LEN 50 | 54 #define NGX_HTTP_AUTOINDEX_NAME_LEN 50 |
49 | 55 |
50 | 56 |
51 static ngx_buf_t *ngx_http_autoindex_html(ngx_http_request_t *r, | 57 static ngx_buf_t *ngx_http_autoindex_html(ngx_http_request_t *r, |
52 ngx_array_t *entries); | 58 ngx_array_t *entries); |
59 static ngx_buf_t *ngx_http_autoindex_json(ngx_http_request_t *r, | |
60 ngx_array_t *entries, ngx_str_t *callback); | |
61 static ngx_int_t ngx_http_autoindex_jsonp_callback(ngx_http_request_t *r, | |
62 ngx_str_t *callback); | |
63 | |
53 static int ngx_libc_cdecl ngx_http_autoindex_cmp_entries(const void *one, | 64 static int ngx_libc_cdecl ngx_http_autoindex_cmp_entries(const void *one, |
54 const void *two); | 65 const void *two); |
55 static ngx_int_t ngx_http_autoindex_error(ngx_http_request_t *r, | 66 static ngx_int_t ngx_http_autoindex_error(ngx_http_request_t *r, |
56 ngx_dir_t *dir, ngx_str_t *name); | 67 ngx_dir_t *dir, ngx_str_t *name); |
68 | |
57 static ngx_int_t ngx_http_autoindex_init(ngx_conf_t *cf); | 69 static ngx_int_t ngx_http_autoindex_init(ngx_conf_t *cf); |
58 static void *ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf); | 70 static void *ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf); |
59 static char *ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf, | 71 static char *ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf, |
60 void *parent, void *child); | 72 void *parent, void *child); |
73 | |
74 | |
75 static ngx_conf_enum_t ngx_http_autoindex_format[] = { | |
76 { ngx_string("html"), NGX_HTTP_AUTOINDEX_HTML }, | |
77 { ngx_string("json"), NGX_HTTP_AUTOINDEX_JSON }, | |
78 { ngx_string("jsonp"), NGX_HTTP_AUTOINDEX_JSONP }, | |
79 { ngx_null_string, 0 } | |
80 }; | |
61 | 81 |
62 | 82 |
63 static ngx_command_t ngx_http_autoindex_commands[] = { | 83 static ngx_command_t ngx_http_autoindex_commands[] = { |
64 | 84 |
65 { ngx_string("autoindex"), | 85 { ngx_string("autoindex"), |
66 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | 86 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, |
67 ngx_conf_set_flag_slot, | 87 ngx_conf_set_flag_slot, |
68 NGX_HTTP_LOC_CONF_OFFSET, | 88 NGX_HTTP_LOC_CONF_OFFSET, |
69 offsetof(ngx_http_autoindex_loc_conf_t, enable), | 89 offsetof(ngx_http_autoindex_loc_conf_t, enable), |
70 NULL }, | 90 NULL }, |
91 | |
92 { ngx_string("autoindex_format"), | |
93 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
94 ngx_conf_set_enum_slot, | |
95 NGX_HTTP_LOC_CONF_OFFSET, | |
96 offsetof(ngx_http_autoindex_loc_conf_t, format), | |
97 &ngx_http_autoindex_format }, | |
71 | 98 |
72 { ngx_string("autoindex_localtime"), | 99 { ngx_string("autoindex_localtime"), |
73 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | 100 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, |
74 ngx_conf_set_flag_slot, | 101 ngx_conf_set_flag_slot, |
75 NGX_HTTP_LOC_CONF_OFFSET, | 102 NGX_HTTP_LOC_CONF_OFFSET, |
124 u_char *last, *filename; | 151 u_char *last, *filename; |
125 size_t len, allocated, root; | 152 size_t len, allocated, root; |
126 ngx_err_t err; | 153 ngx_err_t err; |
127 ngx_buf_t *b; | 154 ngx_buf_t *b; |
128 ngx_int_t rc; | 155 ngx_int_t rc; |
129 ngx_str_t path; | 156 ngx_str_t path, callback; |
130 ngx_dir_t dir; | 157 ngx_dir_t dir; |
131 ngx_uint_t level; | 158 ngx_uint_t level, format; |
132 ngx_pool_t *pool; | 159 ngx_pool_t *pool; |
133 ngx_chain_t out; | 160 ngx_chain_t out; |
134 ngx_array_t entries; | 161 ngx_array_t entries; |
135 ngx_http_autoindex_entry_t *entry; | 162 ngx_http_autoindex_entry_t *entry; |
136 ngx_http_autoindex_loc_conf_t *alcf; | 163 ngx_http_autoindex_loc_conf_t *alcf; |
164 } | 191 } |
165 path.data[path.len] = '\0'; | 192 path.data[path.len] = '\0'; |
166 | 193 |
167 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 194 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
168 "http autoindex: \"%s\"", path.data); | 195 "http autoindex: \"%s\"", path.data); |
196 | |
197 format = alcf->format; | |
198 | |
199 if (format == NGX_HTTP_AUTOINDEX_JSONP) { | |
200 if (ngx_http_autoindex_jsonp_callback(r, &callback) != NGX_OK) { | |
201 return NGX_HTTP_BAD_REQUEST; | |
202 } | |
203 | |
204 if (callback.len == 0) { | |
205 format = NGX_HTTP_AUTOINDEX_JSON; | |
206 } | |
207 } | |
169 | 208 |
170 if (ngx_open_dir(&path, &dir) == NGX_ERROR) { | 209 if (ngx_open_dir(&path, &dir) == NGX_ERROR) { |
171 err = ngx_errno; | 210 err = ngx_errno; |
172 | 211 |
173 if (err == NGX_ENOENT | 212 if (err == NGX_ENOENT |
207 { | 246 { |
208 return ngx_http_autoindex_error(r, &dir, &path); | 247 return ngx_http_autoindex_error(r, &dir, &path); |
209 } | 248 } |
210 | 249 |
211 r->headers_out.status = NGX_HTTP_OK; | 250 r->headers_out.status = NGX_HTTP_OK; |
212 r->headers_out.content_type_len = sizeof("text/html") - 1; | 251 |
213 ngx_str_set(&r->headers_out.content_type, "text/html"); | 252 switch (format) { |
253 | |
254 case NGX_HTTP_AUTOINDEX_JSON: | |
255 ngx_str_set(&r->headers_out.content_type, "application/json"); | |
256 break; | |
257 | |
258 case NGX_HTTP_AUTOINDEX_JSONP: | |
259 ngx_str_set(&r->headers_out.content_type, "application/javascript"); | |
260 break; | |
261 | |
262 default: /* NGX_HTTP_AUTOINDEX_HTML */ | |
263 ngx_str_set(&r->headers_out.content_type, "text/html"); | |
264 break; | |
265 } | |
266 | |
267 r->headers_out.content_type_len = r->headers_out.content_type.len; | |
214 r->headers_out.content_type_lowcase = NULL; | 268 r->headers_out.content_type_lowcase = NULL; |
215 | 269 |
216 rc = ngx_http_send_header(r); | 270 rc = ngx_http_send_header(r); |
217 | 271 |
218 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { | 272 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { |
306 } | 360 } |
307 | 361 |
308 ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1); | 362 ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1); |
309 | 363 |
310 entry->dir = ngx_de_is_dir(&dir); | 364 entry->dir = ngx_de_is_dir(&dir); |
365 entry->file = ngx_de_is_file(&dir); | |
311 entry->mtime = ngx_de_mtime(&dir); | 366 entry->mtime = ngx_de_mtime(&dir); |
312 entry->size = ngx_de_size(&dir); | 367 entry->size = ngx_de_size(&dir); |
313 } | 368 } |
314 | 369 |
315 if (ngx_close_dir(&dir) == NGX_ERROR) { | 370 if (ngx_close_dir(&dir) == NGX_ERROR) { |
321 ngx_qsort(entries.elts, (size_t) entries.nelts, | 376 ngx_qsort(entries.elts, (size_t) entries.nelts, |
322 sizeof(ngx_http_autoindex_entry_t), | 377 sizeof(ngx_http_autoindex_entry_t), |
323 ngx_http_autoindex_cmp_entries); | 378 ngx_http_autoindex_cmp_entries); |
324 } | 379 } |
325 | 380 |
326 b = ngx_http_autoindex_html(r, &entries); | 381 switch (format) { |
382 | |
383 case NGX_HTTP_AUTOINDEX_JSON: | |
384 b = ngx_http_autoindex_json(r, &entries, NULL); | |
385 break; | |
386 | |
387 case NGX_HTTP_AUTOINDEX_JSONP: | |
388 b = ngx_http_autoindex_json(r, &entries, &callback); | |
389 break; | |
390 | |
391 default: /* NGX_HTTP_AUTOINDEX_HTML */ | |
392 b = ngx_http_autoindex_html(r, &entries); | |
393 break; | |
394 } | |
327 | 395 |
328 if (b == NULL) { | 396 if (b == NULL) { |
329 return NGX_ERROR; | 397 return NGX_ERROR; |
330 } | 398 } |
331 | 399 |
606 | 674 |
607 return b; | 675 return b; |
608 } | 676 } |
609 | 677 |
610 | 678 |
679 static ngx_buf_t * | |
680 ngx_http_autoindex_json(ngx_http_request_t *r, ngx_array_t *entries, | |
681 ngx_str_t *callback) | |
682 { | |
683 size_t len; | |
684 ngx_buf_t *b; | |
685 ngx_uint_t i; | |
686 ngx_http_autoindex_entry_t *entry; | |
687 | |
688 len = sizeof("[" CRLF CRLF "]") - 1; | |
689 | |
690 if (callback) { | |
691 len += sizeof("/* callback */" CRLF "();") - 1 + callback->len; | |
692 } | |
693 | |
694 entry = entries->elts; | |
695 | |
696 for (i = 0; i < entries->nelts; i++) { | |
697 entry[i].escape = ngx_escape_json(NULL, entry[i].name.data, | |
698 entry[i].name.len); | |
699 | |
700 len += sizeof("{ }," CRLF) - 1 | |
701 + sizeof("\"name\":\"\"") - 1 | |
702 + entry[i].name.len + entry[i].escape | |
703 + sizeof(", \"type\":\"directory\"") - 1 | |
704 + sizeof(", \"mtime\":\"Wed, 31 Dec 1986 10:00:00 GMT\"") - 1; | |
705 | |
706 if (entry[i].file) { | |
707 len += sizeof(", \"size\":") - 1 + NGX_OFF_T_LEN; | |
708 } | |
709 } | |
710 | |
711 b = ngx_create_temp_buf(r->pool, len); | |
712 if (b == NULL) { | |
713 return NULL; | |
714 } | |
715 | |
716 if (callback) { | |
717 b->last = ngx_cpymem(b->last, "/* callback */" CRLF, | |
718 sizeof("/* callback */" CRLF) - 1); | |
719 | |
720 b->last = ngx_cpymem(b->last, callback->data, callback->len); | |
721 | |
722 *b->last++ = '('; | |
723 } | |
724 | |
725 *b->last++ = '['; | |
726 | |
727 for (i = 0; i < entries->nelts; i++) { | |
728 b->last = ngx_cpymem(b->last, CRLF "{ \"name\":\"", | |
729 sizeof(CRLF "{ \"name\":\"") - 1); | |
730 | |
731 if (entry[i].escape) { | |
732 b->last = (u_char *) ngx_escape_json(b->last, entry[i].name.data, | |
733 entry[i].name.len); | |
734 } else { | |
735 b->last = ngx_cpymem(b->last, entry[i].name.data, | |
736 entry[i].name.len); | |
737 } | |
738 | |
739 b->last = ngx_cpymem(b->last, "\", \"type\":\"", | |
740 sizeof("\", \"type\":\"") - 1); | |
741 | |
742 if (entry[i].dir) { | |
743 b->last = ngx_cpymem(b->last, "directory", sizeof("directory") - 1); | |
744 | |
745 } else if (entry[i].file) { | |
746 b->last = ngx_cpymem(b->last, "file", sizeof("file") - 1); | |
747 | |
748 } else { | |
749 b->last = ngx_cpymem(b->last, "other", sizeof("other") - 1); | |
750 } | |
751 | |
752 b->last = ngx_cpymem(b->last, "\", \"mtime\":\"", | |
753 sizeof("\", \"mtime\":\"") - 1); | |
754 | |
755 b->last = ngx_http_time(b->last, entry[i].mtime); | |
756 | |
757 if (entry[i].file) { | |
758 b->last = ngx_cpymem(b->last, "\", \"size\":", | |
759 sizeof("\", \"size\":") - 1); | |
760 b->last = ngx_sprintf(b->last, "%O", entry[i].size); | |
761 | |
762 } else { | |
763 *b->last++ = '"'; | |
764 } | |
765 | |
766 b->last = ngx_cpymem(b->last, " },", sizeof(" },") - 1); | |
767 } | |
768 | |
769 if (i > 0) { | |
770 b->last--; /* strip last comma */ | |
771 } | |
772 | |
773 b->last = ngx_cpymem(b->last, CRLF "]", sizeof(CRLF "]") - 1); | |
774 | |
775 if (callback) { | |
776 *b->last++ = ')'; *b->last++ = ';'; | |
777 } | |
778 | |
779 return b; | |
780 } | |
781 | |
782 | |
783 static ngx_int_t | |
784 ngx_http_autoindex_jsonp_callback(ngx_http_request_t *r, ngx_str_t *callback) | |
785 { | |
786 u_char *p, c, ch; | |
787 ngx_uint_t i; | |
788 | |
789 if (ngx_http_arg(r, (u_char *) "callback", 8, callback) != NGX_OK) { | |
790 callback->len = 0; | |
791 return NGX_OK; | |
792 } | |
793 | |
794 if (callback->len > 128) { | |
795 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
796 "client sent too long callback name: \"%V\"", callback); | |
797 return NGX_DECLINED; | |
798 } | |
799 | |
800 p = callback->data; | |
801 | |
802 for (i = 0; i < callback->len; i++) { | |
803 ch = p[i]; | |
804 | |
805 c = (u_char) (ch | 0x20); | |
806 if (c >= 'a' && c <= 'z') { | |
807 continue; | |
808 } | |
809 | |
810 if ((ch >= '0' && ch <= '9') || ch == '_' || ch == '.') { | |
811 continue; | |
812 } | |
813 | |
814 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
815 "client sent invalid callback name: \"%V\"", callback); | |
816 | |
817 return NGX_DECLINED; | |
818 } | |
819 | |
820 return NGX_OK; | |
821 } | |
822 | |
823 | |
611 static int ngx_libc_cdecl | 824 static int ngx_libc_cdecl |
612 ngx_http_autoindex_cmp_entries(const void *one, const void *two) | 825 ngx_http_autoindex_cmp_entries(const void *one, const void *two) |
613 { | 826 { |
614 ngx_http_autoindex_entry_t *first = (ngx_http_autoindex_entry_t *) one; | 827 ngx_http_autoindex_entry_t *first = (ngx_http_autoindex_entry_t *) one; |
615 ngx_http_autoindex_entry_t *second = (ngx_http_autoindex_entry_t *) two; | 828 ngx_http_autoindex_entry_t *second = (ngx_http_autoindex_entry_t *) two; |
687 if (conf == NULL) { | 900 if (conf == NULL) { |
688 return NULL; | 901 return NULL; |
689 } | 902 } |
690 | 903 |
691 conf->enable = NGX_CONF_UNSET; | 904 conf->enable = NGX_CONF_UNSET; |
905 conf->format = NGX_CONF_UNSET_UINT; | |
692 conf->localtime = NGX_CONF_UNSET; | 906 conf->localtime = NGX_CONF_UNSET; |
693 conf->exact_size = NGX_CONF_UNSET; | 907 conf->exact_size = NGX_CONF_UNSET; |
694 | 908 |
695 return conf; | 909 return conf; |
696 } | 910 } |
701 { | 915 { |
702 ngx_http_autoindex_loc_conf_t *prev = parent; | 916 ngx_http_autoindex_loc_conf_t *prev = parent; |
703 ngx_http_autoindex_loc_conf_t *conf = child; | 917 ngx_http_autoindex_loc_conf_t *conf = child; |
704 | 918 |
705 ngx_conf_merge_value(conf->enable, prev->enable, 0); | 919 ngx_conf_merge_value(conf->enable, prev->enable, 0); |
920 ngx_conf_merge_uint_value(conf->format, prev->format, | |
921 NGX_HTTP_AUTOINDEX_HTML); | |
706 ngx_conf_merge_value(conf->localtime, prev->localtime, 0); | 922 ngx_conf_merge_value(conf->localtime, prev->localtime, 0); |
707 ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1); | 923 ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1); |
708 | 924 |
709 return NGX_CONF_OK; | 925 return NGX_CONF_OK; |
710 } | 926 } |