├── 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 | --------------------------------------------------------------------------------