├── README.md
├── config
└── ngx_http_unzip_module.c
/README.md:
--------------------------------------------------------------------------------
1 | ## What is this?
2 | A nginx module enabling fetching of files that are stored in zipped archives.
3 |
4 | ## Nginx configuration example
5 |
6 | * file_in_unzip_archivefile - points to the zipped file
7 | * file_in_unzip_extract - file to be extracted from the zipped file
8 | * file_in_unzip - flag activating the module
9 |
10 |
11 | location ~ ^/(.+?\.zip)/(.*)$ {
12 | file_in_unzip_archivefile "$document_root/$1";
13 | file_in_unzip_extract "$2";
14 | file_in_unzip;
15 | }
16 |
17 |
18 |
--------------------------------------------------------------------------------
/config:
--------------------------------------------------------------------------------
1 | ngx_addon_name=ngx_http_unzip_module
2 | HTTP_MODULES="$HTTP_MODULES ngx_http_unzip_module"
3 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_unzip_module.c"
4 | CORE_LIBS="$CORE_LIBS -lzip"
5 |
--------------------------------------------------------------------------------
/ngx_http_unzip_module.c:
--------------------------------------------------------------------------------
1 | /**
2 | * @file ngx_http_unzip_module.c
3 | * @author Bartek Jarocki
4 | * @date Fri Jun 15 10:52:12 2012
5 | *
6 | * @brief This module extract files from unzip archive on the fly
7 | *
8 | * @section LICENSE
9 | *
10 | * Copyright (C) 2012 by Youzee Media Entertainment SL
11 | *
12 | * Permission is hereby granted, free of charge, to any person obtaining a copy
13 | * of this software and associated documentation files (the "Software"), to deal
14 | * in the Software without restriction, including without limitation the rights
15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | * copies of the Software, and to permit persons to whom the Software is
17 | * furnished to do so, subject to the following conditions:
18 | * The above copyright notice and this permission notice shall be included in
19 | * all copies or substantial portions of the Software.
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 | * THE SOFTWARE.
27 | *
28 | */
29 |
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | static char *ngx_http_unzip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
38 | static ngx_int_t ngx_http_unzip_handler(ngx_http_request_t *r);
39 |
40 | typedef struct {
41 | ngx_flag_t file_in_unzip;
42 | ngx_http_complex_value_t *file_in_unzip_archivefile;
43 | ngx_http_complex_value_t *file_in_unzip_extract;
44 | } ngx_http_unzip_loc_conf_t;
45 |
46 |
47 | /**
48 | * This module let you keep your files inside zip archive file
49 | * and serve (unzipping on the fly) them as requested.
50 | */
51 | static ngx_command_t ngx_http_unzip_commands[] = {
52 | {
53 | ngx_string("file_in_unzip"),
54 | NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
55 | ngx_http_unzip,
56 | NGX_HTTP_LOC_CONF_OFFSET,
57 | 0,
58 | NULL
59 | }, {
60 | ngx_string("file_in_unzip_extract"),
61 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
62 | ngx_http_set_complex_value_slot,
63 | NGX_HTTP_LOC_CONF_OFFSET,
64 | offsetof(ngx_http_unzip_loc_conf_t, file_in_unzip_extract),
65 | NULL
66 | }, {
67 | ngx_string("file_in_unzip_archivefile"),
68 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
69 | ngx_http_set_complex_value_slot,
70 | NGX_HTTP_LOC_CONF_OFFSET,
71 | offsetof(ngx_http_unzip_loc_conf_t, file_in_unzip_archivefile),
72 | NULL
73 | },
74 | ngx_null_command
75 | };
76 |
77 | /**
78 | * Create local configuration
79 | *
80 | * @param r
81 | * Pointer to the request structure.
82 | * @return
83 | * Pointer to the configuration structure.
84 | */
85 | static void *
86 | ngx_http_unzip_create_loc_conf(ngx_conf_t *cf) {
87 |
88 | ngx_http_unzip_loc_conf_t *conf;
89 |
90 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_unzip_loc_conf_t));
91 | if (conf == NULL) {
92 | return NGX_CONF_ERROR;
93 | }
94 |
95 | return conf;
96 | }
97 |
98 | /**
99 | * Merge configurations
100 | *
101 | * @param r
102 | * Pointer to the request structure.
103 | * @return
104 | * Status
105 | */
106 | static char *
107 | ngx_http_unzip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) {
108 |
109 | ngx_http_unzip_loc_conf_t *prev = parent;
110 | ngx_http_unzip_loc_conf_t *conf = child;
111 |
112 | if (conf->file_in_unzip_extract == NULL) {
113 | conf->file_in_unzip_extract = prev->file_in_unzip_extract;
114 | }
115 |
116 | if (conf->file_in_unzip_archivefile == NULL) {
117 | conf->file_in_unzip_archivefile = prev->file_in_unzip_archivefile;
118 | }
119 |
120 | return NGX_CONF_OK;
121 | }
122 |
123 | /* The module context. */
124 | static ngx_http_module_t ngx_http_unzip_module_ctx = {
125 | NULL, /* preconfiguration */
126 | NULL, /* postconfiguration */
127 |
128 | NULL, /* create main configuration */
129 | NULL, /* init main configuration */
130 |
131 | NULL, /* create server configuration */
132 | NULL, /* merge server configuration */
133 |
134 | ngx_http_unzip_create_loc_conf, /* create location configuration */
135 | ngx_http_unzip_merge_loc_conf /* merge location configuration */
136 | };
137 |
138 |
139 | /* Module definition. */
140 | ngx_module_t ngx_http_unzip_module = {
141 | NGX_MODULE_V1,
142 | &ngx_http_unzip_module_ctx, /* module context */
143 | ngx_http_unzip_commands, /* module directives */
144 | NGX_HTTP_MODULE, /* module type */
145 | NULL, /* init master */
146 | NULL, /* init module */
147 | NULL, /* init process */
148 | NULL, /* init thread */
149 | NULL, /* exit thread */
150 | NULL, /* exit process */
151 | NULL, /* exit master */
152 | NGX_MODULE_V1_PADDING
153 | };
154 |
155 | /**
156 | * Content handler.
157 | *
158 | * @param r
159 | * Request structure pointer
160 | * @return
161 | * Response status
162 | */
163 | static ngx_int_t ngx_http_unzip_handler(ngx_http_request_t *r)
164 | {
165 | ngx_buf_t *b;
166 | ngx_chain_t out;
167 | ngx_str_t unzip_filename;
168 | ngx_str_t unzip_extract;
169 | struct zip *zip_source;
170 | struct zip_stat zip_st;
171 | struct zip_file *file_in_zip;
172 | int err = 0;
173 | char *unzipfile_path;
174 | char *unzipextract_path;
175 | unsigned char *zip_content;
176 | unsigned int zip_read_bytes;
177 |
178 | ngx_http_unzip_loc_conf_t *unzip_config;
179 | unzip_config = ngx_http_get_module_loc_conf(r, ngx_http_unzip_module);
180 |
181 | /* let's try to get file_in_unzip_archivefile and file_in_unzip_extract from nginx configuration */
182 | if (ngx_http_complex_value(r, unzip_config->file_in_unzip_archivefile, &unzip_filename) != NGX_OK
183 | || ngx_http_complex_value(r, unzip_config->file_in_unzip_extract, &unzip_extract) != NGX_OK) {
184 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to read unzip module configuration settings.");
185 | return NGX_ERROR;
186 | }
187 |
188 | /* we're supporting just GET and HEAD requests */
189 | if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
190 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Only GET and HEAD requests are supported by the unzip module.");
191 | return NGX_HTTP_NOT_ALLOWED;
192 | }
193 |
194 | /* fill path variables with 0 as ngx_string_t doesn't terminate string with 0 */
195 | unzipfile_path = malloc(unzip_filename.len+1);
196 | if (unzipfile_path == NULL) {
197 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate buffer.");
198 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
199 | }
200 |
201 | unzipextract_path = malloc(unzip_extract.len+1);
202 | if (unzipextract_path == NULL) {
203 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate buffer.");
204 | free(unzipfile_path);
205 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
206 | }
207 |
208 | memset(unzipfile_path, 0, unzip_filename.len+1);
209 | memset(unzipextract_path, 0, unzip_extract.len+1);
210 |
211 | /* get path variables terminated with 0 */
212 | strncpy(unzipfile_path, (char *)unzip_filename.data, unzip_filename.len);
213 | strncpy(unzipextract_path, (char *)unzip_extract.data, unzip_extract.len);
214 |
215 | /* try to open archive (zip) file */
216 | if (!(zip_source = zip_open(unzipfile_path, 0, &err))) {
217 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%s : no such archive file.", unzipfile_path);
218 | free(unzipfile_path);
219 | free(unzipextract_path);
220 | return NGX_HTTP_NOT_FOUND;
221 | }
222 |
223 | /* initialize structure */
224 | zip_stat_init(&zip_st);
225 |
226 | /* let's check what's the size of a file. return 404 if we can't stat file inside archive */
227 | if (0 != zip_stat(zip_source, unzipextract_path, 0, &zip_st)) {
228 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no file %s inside %s archive.", unzipextract_path, unzipfile_path);
229 | free(unzipfile_path);
230 | free(unzipextract_path);
231 | zip_close(zip_source);
232 | return NGX_HTTP_NOT_FOUND;
233 | }
234 |
235 | /* allocate buffer for the file content */
236 | if (!(zip_content = ngx_palloc(r->pool, zip_st.size))) {
237 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer memory.");
238 | free(unzipfile_path);
239 | free(unzipextract_path);
240 | zip_close(zip_source);
241 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
242 | }
243 |
244 | /*
245 | * try to open a file that we want - if not return 500 as we know that the file is there (making zip_stat before)
246 | * so let's return 500.
247 | */
248 | if (!(file_in_zip = zip_fopen(zip_source, unzipextract_path, 0))) {
249 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to open %s from %s archive (corrupted?).",
250 | unzipextract_path, unzipfile_path);
251 | free(unzipfile_path);
252 | free(unzipextract_path);
253 | zip_close(zip_source);
254 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
255 | }
256 |
257 | /*
258 | * let's get file content and check if we got all
259 | * we're expecting to get zip_st.size bytes so return 500 if we get something else.
260 | */
261 | if (!(zip_read_bytes = zip_fread(file_in_zip, zip_content, zip_st.size)) || zip_read_bytes != zip_st.size) {
262 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "couldn't get %d bytes of %s from %s archive (corrupted?).",
263 | zip_st.size, unzipextract_path, unzipfile_path);
264 | free(unzipfile_path);
265 | free(unzipextract_path);
266 | zip_fclose(file_in_zip);
267 | zip_close(zip_source);
268 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
269 | }
270 |
271 | /* close both files */
272 | zip_fclose(file_in_zip);
273 | zip_close(zip_source);
274 |
275 | /* let's clean */
276 | free(unzipfile_path);
277 | free(unzipextract_path);
278 |
279 | /* set the content-type header. */
280 | if (ngx_http_set_content_type(r) != NGX_OK) {
281 | r->headers_out.content_type.len = sizeof("text/plain") - 1;
282 | r->headers_out.content_type.data = (u_char *) "text/plain";
283 | }
284 |
285 | /* allocate a new buffer for sending out the reply. */
286 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
287 |
288 | if (b == NULL) {
289 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer.");
290 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
291 | }
292 |
293 | /* insertion in the buffer chain. */
294 | out.buf = b;
295 | out.next = NULL; /* just one buffer */
296 |
297 | b->pos = zip_content;
298 | b->last = zip_content + zip_read_bytes;
299 | b->memory = 1;
300 | b->last_buf = 1;
301 |
302 | /* sending the headers for the reply. */
303 | r->headers_out.status = NGX_HTTP_OK;
304 | r->headers_out.content_length_n = zip_read_bytes;
305 | ngx_http_send_header(r);
306 |
307 | return ngx_http_output_filter(r, &out);
308 | } /* ngx_http_unzip_handler */
309 |
310 | /**
311 | * Configuration setup function that installs the content handler.
312 | *
313 | * @param cf
314 | * Module configuration structure pointer.
315 | * @param cmd
316 | * Module directives structure pointer.
317 | * @param conf
318 | * Module configuration structure pointer.
319 | * @return string
320 | * Status of the configuration setup.
321 | */
322 | static char *ngx_http_unzip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
323 | {
324 | ngx_http_core_loc_conf_t *clcf; /* pointer to core location configuration */
325 |
326 | /* Install the unzip handler. */
327 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
328 | clcf->handler = ngx_http_unzip_handler;
329 |
330 | return NGX_CONF_OK;
331 | } /* ngx_http_unzip */
332 |
333 |
--------------------------------------------------------------------------------