Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Update from Google Code SVN

Zip64 and support for spaces in URLs.
  • Loading branch information...
commit 911b447c2b96bf28cd89d59e00eae3774d8a7897 1 parent 0cc6eb3
Evan Miller authored
View
10 CHANGES
@@ -1,6 +1,12 @@
-Changes with mod_zip 1.1.5 13 Aug 2009
+Changes with mod_zip 1.1.5 03 Sep 2009
- *) Bugfix: compatibility with nginx 0.7.25 and later
+ *) Feature: "If-Range" client header is compared to "Last-Modified" and
+ "ETag" from the upstream file-list server; if there is a match, the
+ "Range" client header is honored, otherwise 200 OK is returned.
+ Using "ETag" requires nginx 0.8.10 or later, or included patch.
+
+ *) Bugfix: compatibility with nginx 0.7.25 and later (but earlier
+ versions are no longer supported)
*) Bugfix: Range works with local, Memcached, and FastCGI files
View
9 README
@@ -10,7 +10,7 @@ To install, compile nginx with the following option:
--add-module=/path/to/this/directory
-nginx 0.6.17 or later is required.
+nginx 0.7.25 or later is required.
The module is activated when the original response (presumably from an
upstream) includes the following HTTP header:
@@ -31,9 +31,12 @@ Files are retrieved and encoded in order. If a file cannot be found or the file
request returns any sort of error, the download is aborted.
The CRC-32 is optional. Put "-" if you don't know the CRC-32; note that in this
-case mod_zip will disable support the "Range" header.
+case mod_zip will disable support for the "Range" header.
-Tip: add a header "Content-Disposition: attachment; filename=foobar.zip" in the
+Tip: Add a header "Content-Disposition: attachment; filename=foobar.zip" in the
upstream response if you would like the client to name the file "foobar.zip"
+Tip 2: To save bandwidth, add a "Last-Modified" header in the upstream response;
+mod_zip will then honor the "If-Range" header from clients.
+
Questions/patches may be directed to Evan Miller, emmiller@gmail.com.
View
391 ngx_http_zip_file.c
@@ -3,6 +3,12 @@
#include "ngx_http_zip_file.h"
#include "ngx_http_zip_file_format.h"
+
+#define NGX_MAX_UINT16_VALUE 0xffff
+
+
+// Chunk templates for fast struct init:
+
static ngx_zip_extra_field_local_t ngx_zip_extra_field_local_template = {
0x5455, /* tag for this extra block type ("UT") */
sizeof(ngx_zip_extra_field_local_t) - 4,
@@ -20,6 +26,27 @@ static ngx_zip_extra_field_central_t ngx_zip_extra_field_central_template = {
0, /* modification time */
};
+static ngx_zip_extra_field_zip64_sizes_only_t ngx_zip_extra_field_zip64_sizes_only_template = {
+ 0x0001, //tag for zip64 extra field
+ sizeof(ngx_zip_extra_field_zip64_sizes_only_t) - 4,
+ 0,
+ 0,
+};
+
+static ngx_zip_extra_field_zip64_offset_only_t ngx_zip_extra_field_zip64_offset_only_template = {
+ 0x0001, //tag for zip64 extra field
+ sizeof(ngx_zip_extra_field_zip64_offset_only_t) - 4,
+ 0,
+};
+
+static ngx_zip_extra_field_zip64_sizes_offset_t ngx_zip_extra_field_zip64_sizes_offset_template = {
+ 0x0001, //tag for zip64 extra field
+ sizeof(ngx_zip_extra_field_zip64_sizes_offset_t) - 4,
+ 0,
+ 0,
+ 0
+};
+
static ngx_zip_data_descriptor_t ngx_zip_data_descriptor_template = {
0x08074b50, /* data descriptor signature */
0, /* crc-32 */
@@ -27,6 +54,13 @@ static ngx_zip_data_descriptor_t ngx_zip_data_descriptor_template = {
0 /* uncompressed size */
};
+static ngx_zip_data_descriptor_zip64_t ngx_zip_data_descriptor_zip64_template = {
+ 0x08074b50, /* data descriptor signature */
+ 0, /* crc-32 */
+ 0, /* compressed size */
+ 0 /* uncompressed size */
+};
+
static ngx_zip_local_file_header_t ngx_zip_local_file_header_template = {
0x04034b50, /* local file header signature */
0x0a, /* version needed to extract */
@@ -34,8 +68,8 @@ static ngx_zip_local_file_header_t ngx_zip_local_file_header_template = {
0, /* compression method */
0, /* last mod file date/time */
0, /* crc-32 */
- 0, /* compressed size */
- 0, /* uncompressed size */
+ 0xffffffff, /* compressed size */
+ 0xffffffff, /* uncompressed size */
0, /* file name length */
sizeof(ngx_zip_extra_field_local_t),
/* extra field length */
@@ -43,48 +77,63 @@ static ngx_zip_local_file_header_t ngx_zip_local_file_header_template = {
static ngx_zip_central_directory_file_header_t ngx_zip_central_directory_file_header_template = {
0x02014b50, /* central file header signature */
- 0x0300, /* version made by */
- 0x0a, /* version needed to extract */
+ zip_version_zip64, /* version made by */
+ zip_version_default, /* version needed to extract */
0x08, /* general purpose bit flag */
0, /* compression method */
0, /* last mod file time */
0, /* crc-32 */
- 0, /* compressed size */
- 0, /* uncompressed size */
+ 0xffffffff, /* compressed size */
+ 0xffffffff, /* uncompressed size */
0, /* file name length */
sizeof(ngx_zip_extra_field_central_t),
/* extra field length */
0, /* file comment length */
0, /* disk number start */
0, /* internal file attributes */
- 0x81a40000, /* external file attributes */
- 0 /* relative offset of local header */
+ 0x81a4000, /* external file attributes */
+ 0xffffffff /* relative offset of local header */
};
static ngx_zip_end_of_central_directory_record_t ngx_zip_end_of_central_directory_record_template = {
0x06054b50, /* end of central dir signature */
0, /* number of this disk */
0, /* number of the disk with the start of the central directory */
- 0, /* total number of entries in the central directory on this disk */
- 0, /* total number of entries in the central directory */
- 0, /* size of the central directory */
- 0, /* offset of start of central directory w.r.t. starting disk # */
+ 0xffff, /* total number of entries in the central directory on this disk */
+ 0xffff, /* total number of entries in the central directory */
+ 0xFFFFFFFF, /* size of the central directory */
+ 0xffffffff, /* offset of start of central directory w.r.t. starting disk # */
0 /* .ZIP file comment length */
};
-static ngx_uint_t ngx_dos_time();
-static void ngx_http_zip_truncate_buffer(ngx_buf_t *b,
- ngx_http_zip_range_t *piece_range, ngx_http_zip_range_t *req_range);
+static ngx_zip_zip64_end_of_central_directory_record_t ngx_zip_zip64_end_of_central_directory_record_template = {
+ 0x06064b50, //signature for zip64 EOCD
+ sizeof(ngx_zip_zip64_end_of_central_directory_record_t)-12, //size of this record (+variable fields, but minus signature and this size field), Size = SizeOfFixedFields + SizeOfVariableData - 12
+ zip_version_zip64, //created by
+ zip_version_zip64, //needed
+ 0, //this disk number
+ 0, // num of disk with start of CD
+ 0,
+ 0,
+ 0,
+ 0, // cd offset with respect to starting disk number
+};
+
+static ngx_zip_zip64_end_of_central_directory_locator_t ngx_zip_zip64_end_of_central_directory_locator_template = {
+ 0x07064b50, //signature
+ 0, // number of disk with start of zip64 end of central directory
+ 0, // offset of central directory
+ 1 //dics number total
+};
+
+//-----------------------------------------------------------------------------------------------------------
-/*
- * Takes a UNIX timestamp and returns a DOS timestamp
- */
+
+// Convert UNIX timestamp to DOS timestamp
static ngx_uint_t ngx_dos_time(time_t t)
{
ngx_tm_t tm;
-
- /* ngx_gmtime does the mon++ and year += 1900 for us */
- ngx_gmtime(t, &tm);
+ ngx_gmtime(t, &tm); // ngx_gmtime does the mon++ and year += 1900 for us
return (tm.ngx_tm_sec >> 1)
+ (tm.ngx_tm_min << 5)
@@ -106,100 +155,102 @@ ngx_http_zip_truncate_buffer(ngx_buf_t *b,
}
}
-off_t
-ngx_http_zip_calculate_central_directory_size(off_t files_n,
- off_t filename_s)
-{
- return
- files_n * sizeof(ngx_zip_central_directory_file_header_t)
- + filename_s
- + files_n * sizeof(ngx_zip_extra_field_central_t)
- + sizeof(ngx_zip_end_of_central_directory_record_t);
-}
+// make our proposed ZIP-file chunk map
ngx_int_t
ngx_http_zip_generate_pieces(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx)
{
ngx_uint_t i, piece_i;
off_t offset = 0;
- off_t filenames_len = 0;
ngx_http_zip_file_t *file;
ngx_http_zip_piece_t *header_piece, *file_piece, *trailer_piece, *cd_piece;
- ctx->pieces_n = ctx->files.nelts * (2 + ctx->missing_crc32) + 1;
+ // pieces: for each file: header, data, footer (if needed) -> 2 or 3 per file
+ // plus file footer (CD + [zip64 end + zip64 locator +] end of cd) in one chunk
+ ctx->pieces_n = ctx->files.nelts * (2 + (!!ctx->missing_crc32)) + 1;
- if ((ctx->pieces = ngx_palloc(r->pool, sizeof(ngx_http_zip_piece_t) *
- ctx->pieces_n)) == NULL) {
+ if ((ctx->pieces = ngx_palloc(r->pool, sizeof(ngx_http_zip_piece_t) * ctx->pieces_n)) == NULL)
return NGX_ERROR;
- }
-
- for (piece_i=i=0; i<ctx->files.nelts; i++) {
+
+ ctx->cd_size = 0;
+ for (piece_i = i = 0; i < ctx->files.nelts; i++) {
file = &((ngx_http_zip_file_t *)ctx->files.elts)[i];
- filenames_len += file->filename.len;
file->offset = offset;
+ if(offset >= NGX_MAX_UINT32_VALUE)
+ ctx->zip64_used = file->need_zip64_offset = 1;
+ if(file->size >= NGX_MAX_UINT32_VALUE)
+ ctx->zip64_used = file->need_zip64 = 1;
+
+ ctx->cd_size += sizeof(ngx_zip_central_directory_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_central_t)
+ + (file->need_zip64_offset ?
+ (file->need_zip64 ? sizeof(ngx_zip_extra_field_zip64_sizes_offset_t) : sizeof(ngx_zip_extra_field_zip64_offset_only_t)) :
+ (file->need_zip64 ? sizeof(ngx_zip_extra_field_zip64_sizes_only_t) : 0)
+ );
+
header_piece = &ctx->pieces[piece_i++];
header_piece->type = zip_header_piece;
header_piece->file = file;
header_piece->range.start = offset;
header_piece->range.end = offset += sizeof(ngx_zip_local_file_header_t)
- + file->filename.len + sizeof(ngx_zip_extra_field_local_t);
+ + file->filename.len + sizeof(ngx_zip_extra_field_local_t) + (file->need_zip64? sizeof(ngx_zip_extra_field_zip64_sizes_only_t):0);
file_piece = &ctx->pieces[piece_i++];
file_piece->type = zip_file_piece;
file_piece->file = file;
file_piece->range.start = offset;
- file_piece->range.end = offset += file->size;
+ file_piece->range.end = offset += file->size; //!note: (sizeless chunks): we need file size here / or mark it and modify ranges after
- if (ctx->missing_crc32) {
+ if (file->missing_crc32) { // if incomplete header -> add footer with that info to file
trailer_piece = &ctx->pieces[piece_i++];
trailer_piece->type = zip_trailer_piece;
trailer_piece->file = file;
trailer_piece->range.start = offset;
- trailer_piece->range.end = offset += sizeof(ngx_zip_data_descriptor_t);
+ trailer_piece->range.end = offset += file->need_zip64? sizeof(ngx_zip_data_descriptor_zip64_t) : sizeof(ngx_zip_data_descriptor_t);
+ //!!TODO: if we want Ranges support - here we know it is impossible for this set
+ //? check conf/some state and abort?
}
}
- cd_piece = &ctx->pieces[piece_i];
+ ctx->zip64_used |= offset >= NGX_MAX_UINT32_VALUE || ctx->files.nelts >= NGX_MAX_UINT16_VALUE;
+
+ ctx->cd_size += sizeof(ngx_zip_end_of_central_directory_record_t);
+ if (ctx->zip64_used)
+ ctx->cd_size += sizeof(ngx_zip_zip64_end_of_central_directory_record_t) + sizeof(ngx_zip_zip64_end_of_central_directory_locator_t);
+
+
+ cd_piece = &ctx->pieces[piece_i++];
cd_piece->type = zip_central_directory_piece;
cd_piece->range.start = offset;
- cd_piece->range.end = offset +=
- ngx_http_zip_calculate_central_directory_size(ctx->files.nelts,
- filenames_len);
+ cd_piece->range.end = offset += ctx->cd_size;
+
+ ctx->pieces_n = piece_i; //!! nasty hack (truncating allocated array without reallocation)
ctx->archive_size = offset;
return NGX_OK;
}
-/* Craft a header for a file in a ZIP archive
- */
+// make Local File Header chunk with extra fields
ngx_chain_t*
ngx_http_zip_file_header_chain_link(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx,
ngx_http_zip_piece_t *piece, ngx_http_zip_range_t *range)
{
ngx_chain_t *link;
ngx_buf_t *b;
- size_t len;
+
ngx_http_zip_file_t *file = piece->file;
ngx_zip_extra_field_local_t extra_field_local;
+ ngx_zip_extra_field_zip64_sizes_only_t extra_field_zip64;
ngx_zip_local_file_header_t local_file_header;
- if ((link = ngx_alloc_chain_link(r->pool)) == NULL) {
- return NULL;
- }
-
- if ((b = ngx_calloc_buf(r->pool)) == NULL) {
- return NULL;
- }
-
- len = sizeof(ngx_zip_local_file_header_t)
- + file->filename.len + sizeof(ngx_zip_extra_field_local_t);
+ size_t len = sizeof(ngx_zip_local_file_header_t) + file->filename.len
+ + sizeof(ngx_zip_extra_field_local_t) + (file->need_zip64? sizeof(ngx_zip_extra_field_zip64_sizes_only_t):0);
- if ((b->pos = ngx_pcalloc(r->pool, len)) == NULL) {
+ if ((link = ngx_alloc_chain_link(r->pool)) == NULL || (b = ngx_calloc_buf(r->pool)) == NULL
+ || (b->pos = ngx_pcalloc(r->pool, len)) == NULL)
return NULL;
- }
-
+
b->memory = 1;
b->last = b->pos + len;
@@ -214,10 +265,18 @@ ngx_http_zip_file_header_chain_link(ngx_http_request_t *r, ngx_http_zip_ctx_t *c
local_file_header = ngx_zip_local_file_header_template;
local_file_header.mtime = file->dos_time;
- local_file_header.compressed_size = file->size;
- local_file_header.uncompressed_size = file->size;
local_file_header.filename_len = file->filename.len;
- if (!ctx->missing_crc32) {
+ if (file->need_zip64) {
+ local_file_header.version = zip_version_zip64;
+ local_file_header.extra_field_len = sizeof(ngx_zip_extra_field_zip64_sizes_only_t) + sizeof(ngx_zip_extra_field_local_t);
+ extra_field_zip64 = ngx_zip_extra_field_zip64_sizes_only_template;
+ extra_field_zip64.uncompressed_size = extra_field_zip64.compressed_size = file->size;
+ } else {
+ local_file_header.compressed_size = file->size;
+ local_file_header.uncompressed_size = file->size;
+ }
+
+ if (!file->missing_crc32) {
local_file_header.flags = 0;
local_file_header.crc32 = file->crc32;
}
@@ -226,12 +285,13 @@ ngx_http_zip_file_header_chain_link(ngx_http_request_t *r, ngx_http_zip_ctx_t *c
extra_field_local.mtime = file->unix_time;
extra_field_local.atime = file->unix_time;
- ngx_memcpy(b->pos,
- &local_file_header, sizeof(ngx_zip_local_file_header_t));
- ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t),
- file->filename.data, file->filename.len);
+ ngx_memcpy(b->pos, &local_file_header, sizeof(ngx_zip_local_file_header_t));
+ ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t), file->filename.data, file->filename.len);
ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t) + file->filename.len,
&extra_field_local, sizeof(ngx_zip_extra_field_local_t));
+ if (file->need_zip64)
+ ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_local_t),
+ &extra_field_zip64, sizeof(ngx_zip_extra_field_zip64_sizes_only_t));
ngx_http_zip_truncate_buffer(b, &piece->range, range);
@@ -241,40 +301,37 @@ ngx_http_zip_file_header_chain_link(ngx_http_request_t *r, ngx_http_zip_ctx_t *c
return link;
}
-/* Craft a trailer for a file in a ZIP archive
- */
+
+// make buffer with 32/64 bit Data Descriptor chunk, this follows files with incomplete headers
ngx_chain_t*
-ngx_http_zip_data_descriptor_chain_link(ngx_http_request_t *r,
- ngx_http_zip_piece_t *piece, ngx_http_zip_range_t *range)
+ngx_http_zip_data_descriptor_chain_link(ngx_http_request_t *r, ngx_http_zip_piece_t *piece, ngx_http_zip_range_t *range)
{
ngx_chain_t *link;
ngx_buf_t *b;
ngx_http_zip_file_t *file = piece->file;
- ngx_zip_data_descriptor_t data_descriptor;
-
- if ((link = ngx_alloc_chain_link(r->pool)) == NULL) {
- return NULL;
- }
-
- if ((b = ngx_calloc_buf(r->pool)) == NULL) {
- return NULL;
- }
-
- b->pos = ngx_palloc(r->pool, sizeof(ngx_zip_data_descriptor_t));
- if (b->pos == NULL) {
+ size_t struct_size = file->need_zip64? sizeof(ngx_zip_data_descriptor_zip64_t) : sizeof(ngx_zip_data_descriptor_t);
+ union {
+ ngx_zip_data_descriptor_t descriptor;
+ ngx_zip_data_descriptor_zip64_t descriptor64;
+ } data;
+
+ if ((link = ngx_alloc_chain_link(r->pool)) == NULL || (b = ngx_calloc_buf(r->pool)) == NULL
+ || (b->pos = ngx_palloc(r->pool, struct_size)) == NULL)
return NULL;
- }
-
b->memory = 1;
- b->last = b->pos + sizeof(ngx_zip_data_descriptor_t);
-
- data_descriptor = ngx_zip_data_descriptor_template;
- data_descriptor.crc32 = file->crc32;
- data_descriptor.compressed_size = file->size;
- data_descriptor.uncompressed_size = file->size;
-
- ngx_memcpy(b->pos, &data_descriptor, sizeof(ngx_zip_data_descriptor_t));
+ b->last = b->pos + struct_size;
+
+ if (!file->need_zip64) {
+ data.descriptor = ngx_zip_data_descriptor_template;
+ data.descriptor.crc32 = file->crc32;
+ data.descriptor.compressed_size = data.descriptor.uncompressed_size = file->size;
+ } else {
+ data.descriptor64 = ngx_zip_data_descriptor_zip64_template;
+ data.descriptor64.crc32 = file->crc32;
+ data.descriptor64.compressed_size = data.descriptor64.uncompressed_size = file->size;
+ }
+ ngx_memcpy(b->pos, &data, struct_size);
ngx_http_zip_truncate_buffer(b, &piece->range, range);
link->buf = b;
@@ -283,102 +340,142 @@ ngx_http_zip_data_descriptor_chain_link(ngx_http_request_t *r,
return link;
}
-/* Attach the trailer to the whole ZIP archive */
+
+//make archive footer: Central Directory, Zip64 Central Directory End, Zip64 locator and Central Directory end chunks
ngx_chain_t *
-ngx_http_zip_central_directory_chain_link(ngx_http_request_t *r,
- ngx_http_zip_ctx_t *ctx, ngx_http_zip_piece_t *piece, ngx_http_zip_range_t *range)
+ngx_http_zip_central_directory_chain_link(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx, ngx_http_zip_piece_t *piece, ngx_http_zip_range_t *range)
{
+ //nb: this is to be called only after 'generate pieces'
ngx_chain_t *trailer;
ngx_buf_t *trailer_buf;
- size_t cd_len = 0, filenames_len = 0;
u_char *p;
+ off_t cd_size;
ngx_uint_t i;
- ngx_http_zip_file_t *file;
ngx_array_t *files;
ngx_zip_end_of_central_directory_record_t eocdr;
+ ngx_zip_zip64_end_of_central_directory_record_t eocdr64;
+ ngx_zip_zip64_end_of_central_directory_locator_t locator64;
- if (ctx == NULL) {
+ if (!ctx || !ctx->cd_size || (trailer = ngx_alloc_chain_link(r->pool)) == NULL
+ || (trailer_buf = ngx_calloc_buf(r->pool)) == NULL
+ || (p = ngx_palloc(r->pool, ctx->cd_size)) == NULL)
return NULL;
- }
-
+
files = &ctx->files;
-
- if ((trailer = ngx_alloc_chain_link(r->pool)) == NULL) {
- return NULL;
- }
-
- if ((trailer_buf = ngx_calloc_buf(r->pool)) == NULL) {
- return NULL;
- }
-
trailer->buf = trailer_buf;
trailer->next = NULL;
- for (i=0; i < files->nelts; i++) {
- file = &((ngx_http_zip_file_t *)files->elts)[i];
- filenames_len += file->filename.len;
- }
-
- cd_len = ngx_http_zip_calculate_central_directory_size(files->nelts,
- filenames_len);
-
- if ((p = ngx_palloc(r->pool, cd_len)) == NULL) {
- return NULL;
- }
-
trailer_buf->pos = p;
- trailer_buf->last = p + cd_len;
+ trailer_buf->last = p + ctx->cd_size;
trailer_buf->last_buf = 1;
trailer_buf->sync = 1;
trailer_buf->memory = 1;
- for (i=0; i < files->nelts; i++) {
- p = ngx_http_zip_write_central_directory_entry(p,
- &((ngx_http_zip_file_t *)files->elts)[i], ctx);
- }
+ for (i = 0; i < files->nelts; i++)
+ p = ngx_http_zip_write_central_directory_entry(p, &((ngx_http_zip_file_t *)files->elts)[i], ctx);
eocdr = ngx_zip_end_of_central_directory_record_template;
- eocdr.disk_entries_n = files->nelts;
- eocdr.entries_n = files->nelts;
- eocdr.size = cd_len - sizeof(ngx_zip_end_of_central_directory_record_t);
- eocdr.offset = piece->range.start;
+ if (files->nelts < NGX_MAX_UINT16_VALUE) {
+ eocdr.disk_entries_n = files->nelts;
+ eocdr.entries_n = files->nelts;
+ }
+
+ cd_size = ctx->cd_size - sizeof(ngx_zip_end_of_central_directory_record_t)
+ - (!!ctx->zip64_used)*(sizeof(ngx_zip_zip64_end_of_central_directory_record_t)
+ + sizeof(ngx_zip_zip64_end_of_central_directory_locator_t));
+
+ if (cd_size < NGX_MAX_UINT32_VALUE)
+ eocdr.size = cd_size;
+ if (piece->range.start < NGX_MAX_UINT32_VALUE)
+ eocdr.offset = piece->range.start;
+
+ if (ctx->zip64_used) {
+ eocdr64 = ngx_zip_zip64_end_of_central_directory_record_template;
+ eocdr64.cd_n_entries_on_this_disk = eocdr64.cd_n_entries_total = files->nelts;
+ eocdr64.cd_size = cd_size;
+ eocdr64.cd_offset = piece->range.start;
+
+ ngx_memcpy(p, &eocdr64, sizeof(ngx_zip_zip64_end_of_central_directory_record_t));
+ p += sizeof(ngx_zip_zip64_end_of_central_directory_record_t);
+ locator64 = ngx_zip_zip64_end_of_central_directory_locator_template;
+ locator64.cd_relative_offset = piece->range.start + eocdr64.cd_size;
+ ngx_memcpy(p, &locator64, sizeof(ngx_zip_zip64_end_of_central_directory_locator_t));
+ p += sizeof(ngx_zip_zip64_end_of_central_directory_locator_t);
+ }
+
ngx_memcpy(p, &eocdr, sizeof(ngx_zip_end_of_central_directory_record_t));
ngx_http_zip_truncate_buffer(trailer->buf, &piece->range, range);
-
return trailer;
}
+
u_char *
ngx_http_zip_write_central_directory_entry(u_char *p, ngx_http_zip_file_t *file,
ngx_http_zip_ctx_t *ctx)
{
ngx_zip_extra_field_central_t extra_field_central;
ngx_zip_central_directory_file_header_t central_directory_file_header;
+ ngx_zip_extra_field_zip64_offset_only_t extra_zip64_offset;
+ ngx_zip_extra_field_zip64_sizes_offset_t extra_zip64_offset_size;
+ ngx_zip_extra_field_zip64_sizes_only_t extra_zip64_size;
+ void* extra_zip64_ptr = NULL; //!!
+ size_t extra_zip64_ptr_size = 0;
central_directory_file_header = ngx_zip_central_directory_file_header_template;
central_directory_file_header.mtime = file->dos_time;
central_directory_file_header.crc32 = file->crc32;
- central_directory_file_header.compressed_size = file->size;
- central_directory_file_header.uncompressed_size = file->size;
+
+ if (!file->need_zip64) {
+ central_directory_file_header.compressed_size = file->size;
+ central_directory_file_header.uncompressed_size = file->size;
+ }
central_directory_file_header.filename_len = file->filename.len;
- central_directory_file_header.offset = file->offset;
- if (!ctx->missing_crc32) {
+ if (!file->need_zip64_offset)
+ central_directory_file_header.offset = file->offset;
+ if (!file->missing_crc32)
central_directory_file_header.flags = 0;
- }
+ if (file->need_zip64) {
+ central_directory_file_header.version_needed = zip_version_zip64;
+ if (file->need_zip64_offset){
+ extra_zip64_offset_size = ngx_zip_extra_field_zip64_sizes_offset_template;
+ extra_zip64_offset_size.relative_header_offset = file->offset;
+ extra_zip64_offset_size.compressed_size = extra_zip64_offset_size.uncompressed_size = file->size;
+ extra_zip64_ptr = &extra_zip64_offset_size;
+ extra_zip64_ptr_size = sizeof(extra_zip64_offset_size);
+ } else { //zip64 only
+ extra_zip64_size = ngx_zip_extra_field_zip64_sizes_only_template;
+ extra_zip64_size.compressed_size = extra_zip64_size.uncompressed_size = file->size;
+ extra_zip64_ptr = &extra_zip64_size;
+ extra_zip64_ptr_size = sizeof(extra_zip64_size);
+ }
+ } else {
+ if (file->need_zip64_offset){
+ extra_zip64_offset = ngx_zip_extra_field_zip64_offset_only_template;
+ extra_zip64_offset.relative_header_offset = file->offset;
+ extra_zip64_ptr = &extra_zip64_offset;
+ extra_zip64_ptr_size = sizeof(extra_zip64_offset);
+ }
+ }
+ central_directory_file_header.extra_field_len=sizeof(ngx_zip_extra_field_central_t)+extra_zip64_ptr_size;
extra_field_central = ngx_zip_extra_field_central_template;
extra_field_central.mtime = file->unix_time;
- ngx_memcpy(p, &central_directory_file_header,
- sizeof(ngx_zip_central_directory_file_header_t));
+ ngx_memcpy(p, &central_directory_file_header, sizeof(ngx_zip_central_directory_file_header_t));
+ p += sizeof(ngx_zip_central_directory_file_header_t);
- ngx_memcpy(p += sizeof(ngx_zip_central_directory_file_header_t),
- file->filename.data, file->filename.len);
+ ngx_memcpy(p, file->filename.data, file->filename.len);
+ p += file->filename.len;
- ngx_memcpy(p += file->filename.len,
- &extra_field_central, sizeof(ngx_zip_extra_field_central_t));
+ ngx_memcpy(p, &extra_field_central, sizeof(ngx_zip_extra_field_central_t));
+ p += sizeof(ngx_zip_extra_field_central_t);
+
+ if (extra_zip64_ptr) {
+ ngx_memcpy(p, extra_zip64_ptr, extra_zip64_ptr_size);
+ p += extra_zip64_ptr_size;
+ }
- return p + sizeof(ngx_zip_extra_field_central_t);
+ return p;
}
View
76 ngx_http_zip_file_format.h
@@ -5,23 +5,58 @@
/* byte-align structs to conform to ZIP format */
#pragma pack(push, 1)
+#define zip_version_default 10
+#define zip_version_zip64 45
+
typedef struct {
- uint16_t tag;
+ uint16_t tag; //0x5455
uint16_t size;
uint8_t info;
uint32_t mtime;
uint32_t atime;
-} ngx_zip_extra_field_local_t;
+} ngx_zip_extra_field_local_t; // extended timestamp
typedef struct {
- uint16_t tag;
+ uint16_t tag; //0x5455
uint16_t size;
uint8_t info;
uint32_t mtime;
} ngx_zip_extra_field_central_t;
+typedef struct { // not entirely writen...
+ uint16_t tag; //0x0001
+ uint16_t size; // size of this record (32)
+ uint64_t uncompressed_size; //!! in all other places in spec uncompressed follow compressed!
+ uint64_t compressed_size;
+ //these for CD:
+ uint64_t relative_header_offset; //offset of local header record (cd)
+ uint32_t disc_start; // no of disc where file starts (cd)
+} ngx_zip_extra_field_zip64_full_t;
+
typedef struct {
- uint32_t signature;
+ uint16_t tag; //0x0001
+ uint16_t size; //0x14
+ uint64_t uncompressed_size;
+ uint64_t compressed_size;
+} ngx_zip_extra_field_zip64_sizes_only_t;
+
+typedef struct {
+ uint16_t tag; //0x0001
+ uint16_t size; //0x0C
+ uint64_t relative_header_offset;
+} ngx_zip_extra_field_zip64_offset_only_t;
+
+typedef struct {
+ uint16_t tag; //0x0001
+ uint16_t size; //0x1C
+ uint64_t uncompressed_size;
+ uint64_t compressed_size;
+ uint64_t relative_header_offset;
+} ngx_zip_extra_field_zip64_sizes_offset_t;
+
+
+typedef struct {
+ uint32_t signature; //0x08074b50
uint32_t crc32;
uint32_t compressed_size;
uint32_t uncompressed_size;
@@ -29,6 +64,14 @@ typedef struct {
typedef struct {
uint32_t signature;
+ uint32_t crc32;
+ uint64_t compressed_size;
+ uint64_t uncompressed_size;
+} ngx_zip_data_descriptor_zip64_t;
+
+
+typedef struct {
+ uint32_t signature; //0x04034b50
uint16_t version;
uint16_t flags;
uint16_t compression_method;
@@ -40,8 +83,9 @@ typedef struct {
uint16_t extra_field_len;
} ngx_zip_local_file_header_t;
+
typedef struct {
- uint32_t signature;
+ uint32_t signature; //0x02014b50
uint16_t version_made_by;
uint16_t version_needed;
uint16_t flags;
@@ -60,7 +104,7 @@ typedef struct {
} ngx_zip_central_directory_file_header_t;
typedef struct {
- uint32_t signature;
+ uint32_t signature; //0x06054b50
uint16_t disk_n;
uint16_t cd_disk_n;
uint16_t disk_entries_n;
@@ -70,4 +114,24 @@ typedef struct {
uint16_t comment_len;
} ngx_zip_end_of_central_directory_record_t;
+typedef struct {
+ uint32_t signature; // 0x06064b50
+ uint64_t size; //of this record (+variable fields, but minus signature and this size field), Size = SizeOfFixedFields + SizeOfVariableData - 12
+ uint16_t version_made_by;
+ uint16_t version_needed;
+ uint32_t disk_n;
+ uint32_t cd_disk_n; // num of disk with start of CD
+ uint64_t cd_n_entries_on_this_disk;
+ uint64_t cd_n_entries_total;
+ uint64_t cd_size;
+ uint64_t cd_offset; // cd offset with respect to starting disk number
+ //variable fields go here
+} ngx_zip_zip64_end_of_central_directory_record_t;
+
+typedef struct {
+ uint32_t signature; //0x07064b50
+ uint32_t z64_cd_disk_n; // number of disk with start of zip64 end of central directory
+ uint64_t cd_relative_offset;
+ uint32_t disks_total_n;
+} ngx_zip_zip64_end_of_central_directory_locator_t;
#pragma pack(pop)
View
302 ngx_http_zip_module.c
@@ -49,7 +49,6 @@ static ngx_int_t ngx_http_zip_send_final_boundary(ngx_http_request_t *r,
ngx_http_zip_ctx_t *ctx);
static ngx_int_t ngx_http_zip_init(ngx_conf_t *cf);
-static ngx_int_t ngx_http_zip_init_crc32(ngx_array_t *files);
static ngx_int_t ngx_http_zip_main_request_header_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_zip_subrequest_header_filter(ngx_http_request_t *r);
@@ -107,8 +106,7 @@ static ngx_chain_t *ngx_chain_last_link(ngx_chain_t *chain_link)
}
static ngx_int_t
-ngx_http_zip_discard_chain(ngx_http_request_t *r,
- ngx_chain_t *in)
+ngx_http_zip_discard_chain(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_chain_t *chain_link;
@@ -127,7 +125,7 @@ ngx_http_zip_discard_chain(ngx_http_request_t *r,
static ngx_int_t
ngx_http_zip_ranges_intersect(ngx_http_zip_range_t *range1, ngx_http_zip_range_t *range2)
{
- return (range1 == NULL) || (range2 == NULL) ||
+ return (range1 == NULL) || (range2 == NULL) ||
!(range1->start >= range2->end || range2->start >= range1->end);
}
@@ -142,15 +140,11 @@ static ngx_int_t ngx_http_zip_copy_unparsed_request(ngx_http_request_t *r,
len = ngx_chain_length(in);
- if (old_unparsed_request != NULL) {
+ if (old_unparsed_request != NULL)
len += old_unparsed_request->len;
- }
-
- if ((ctx->unparsed_request = ngx_palloc(r->pool, sizeof(ngx_str_t))) == NULL) {
- return NGX_ERROR;
- }
-
- if ((ctx->unparsed_request->data = ngx_palloc(r->pool, len)) == NULL) {
+
+ if ((ctx->unparsed_request = ngx_palloc(r->pool, sizeof(ngx_str_t))) == NULL
+ || (ctx->unparsed_request->data = ngx_palloc(r->pool, len)) == NULL) {
return NGX_ERROR;
}
@@ -160,8 +154,7 @@ static ngx_int_t ngx_http_zip_copy_unparsed_request(ngx_http_request_t *r,
}
for (chain_link = in; chain_link; chain_link = chain_link->next ) {
- ngx_memcpy(ctx->unparsed_request->data + offset, chain_link->buf->pos,
- chain_link->buf->last - chain_link->buf->pos);
+ ngx_memcpy(ctx->unparsed_request->data + offset, chain_link->buf->pos, chain_link->buf->last - chain_link->buf->pos);
offset += chain_link->buf->last - chain_link->buf->pos;
}
@@ -178,12 +171,10 @@ static ngx_int_t ngx_http_zip_copy_unparsed_request(ngx_http_request_t *r,
*/
static ngx_int_t ngx_http_zip_header_filter(ngx_http_request_t *r)
{
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: entering header filter");
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: entering header filter");
- if (r != r->main) {
+ if (r != r->main)
return ngx_http_zip_subrequest_header_filter(r);
- }
return ngx_http_zip_main_request_header_filter(r);
}
@@ -194,46 +185,26 @@ ngx_http_zip_main_request_header_filter(ngx_http_request_t *r)
ngx_http_variable_value_t *vv;
ngx_http_zip_ctx_t *ctx;
- if ((ctx = ngx_http_get_module_ctx(r, ngx_http_zip_module)) != NULL) {
+ if ((ctx = ngx_http_get_module_ctx(r, ngx_http_zip_module)) != NULL)
return ngx_http_next_header_filter(r);
- }
- if ((vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t))) == NULL) {
+ if ((vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t))) == NULL)
return NGX_ERROR;
- }
-
+
/* Look for X-Archive-Files */
- if (ngx_http_upstream_header_variable(r, vv,
- (uintptr_t)(&ngx_http_zip_header_variable_name)) != NGX_OK
- || vv->not_found
- || ngx_strncmp(vv->data, "zip", sizeof("zip") - 1) != 0)
- {
+ if (ngx_http_upstream_header_variable(r, vv, (uintptr_t)(&ngx_http_zip_header_variable_name)) != NGX_OK
+ || vv->not_found || ngx_strncmp(vv->data, "zip", sizeof("zip") - 1) != 0)
return ngx_http_next_header_filter(r);
- }
-
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: X-Archive-Files found");
-
- if ((ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_zip_ctx_t))) == NULL) {
- return NGX_ERROR;
- }
-
- if (ngx_array_init(&ctx->files, r->pool, 1, sizeof(ngx_http_zip_file_t))
- == NGX_ERROR)
- {
- return NGX_ERROR;
- }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: X-Archive-Files found");
- if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_zip_range_t))
- == NGX_ERROR)
- {
+ if ((ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_zip_ctx_t))) == NULL
+ || ngx_array_init(&ctx->files, r->pool, 1, sizeof(ngx_http_zip_file_t)) == NGX_ERROR
+ || ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_zip_range_t)) == NGX_ERROR)
return NGX_ERROR;
- }
-
+
ngx_http_set_ctx(r, ctx, ngx_http_zip_module);
- ctx->wait = NULL; // not waiting initially
-
return NGX_OK;
}
@@ -244,8 +215,7 @@ ngx_http_zip_subrequest_header_filter(ngx_http_request_t *r)
ctx = ngx_http_get_module_ctx(r->main, ngx_http_zip_module);
if (ctx != NULL) {
- if (r->headers_out.status != NGX_HTTP_OK
- && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) {
+ if (r->headers_out.status != NGX_HTTP_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"mod_zip: a subrequest returned %d, aborting...",
r->headers_out.status);
@@ -262,6 +232,7 @@ ngx_http_zip_subrequest_header_filter(ngx_http_request_t *r)
static ngx_int_t
ngx_http_zip_set_headers(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx)
{
+ time_t if_range, last_modified;
if (ngx_http_zip_add_cache_control(r) == NGX_ERROR) {
return NGX_ERROR;
@@ -270,7 +241,6 @@ ngx_http_zip_set_headers(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx)
r->headers_out.content_type.len = sizeof(NGX_ZIP_MIME_TYPE) - 1;
r->headers_out.content_type.data = (u_char *)NGX_ZIP_MIME_TYPE;
ngx_http_clear_content_length(r);
- ngx_http_clear_last_modified(r);
if (ctx->missing_crc32) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -280,7 +250,7 @@ ngx_http_zip_set_headers(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx)
r->headers_out.content_length_n = ctx->archive_size;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"mod_zip: Archive will be %O bytes", ctx->archive_size);
- if (r->headers_in.range && !r->headers_in.if_range) {
+ if (r->headers_in.range) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"mod_zip: Range found");
if (ctx->missing_crc32) {
@@ -288,6 +258,42 @@ ngx_http_zip_set_headers(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx)
"mod_zip: Missing checksums, ignoring Range");
return NGX_OK;
}
+ if (r->headers_in.if_range && r->upstream) {
+ if_range = ngx_http_parse_time(r->headers_in.if_range->value.data,
+ r->headers_in.if_range->value.len);
+ if (if_range == NGX_ERROR) { /* treat as ETag */
+ if (r->upstream->headers_in.etag) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "mod_zip: If-Range = %V, ETag = %V",
+ &r->headers_in.if_range->value, &r->upstream->headers_in.etag->value);
+ if (r->upstream->headers_in.etag->value.len != r->headers_in.if_range->value.len
+ || ngx_strncmp(r->upstream->headers_in.etag->value.data,
+ r->headers_in.if_range->value.data,
+ r->headers_in.if_range->value.len)) {
+ return NGX_OK;
+ }
+ } else {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "mod_zip: No ETag from upstream");
+ return NGX_OK;
+ }
+ } else { /* treat as modification time */
+ if (r->upstream->headers_in.last_modified) {
+ last_modified = ngx_http_parse_time(r->upstream->headers_in.last_modified->value.data,
+ r->upstream->headers_in.last_modified->value.len);
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "mod_zip: If-Range = %d, Last-Modified = %d",
+ if_range, last_modified);
+ if (if_range != last_modified && last_modified != -1) {
+ return NGX_OK;
+ }
+ } else {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "mod_zip: No Last-Modified from upstream");
+ return NGX_OK;
+ }
+ }
+ }
if (ngx_http_zip_parse_range(r, &r->headers_in.range->value, ctx)
== NGX_ERROR) {
r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
@@ -318,17 +324,14 @@ ngx_http_zip_set_headers(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx)
}
static ngx_int_t
-ngx_http_zip_body_filter(ngx_http_request_t *r,
- ngx_chain_t *in)
+ngx_http_zip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
if (r != r->main) {
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: entering subrequest body filter");
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: entering subrequest body filter");
return ngx_http_zip_subrequest_body_filter(r, in);
}
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: entering main request body filter");
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: entering main request body filter");
return ngx_http_zip_main_request_body_filter(r, in);
}
@@ -343,28 +346,23 @@ ngx_http_zip_subrequest_body_filter(ngx_http_request_t *r,
ctx = ngx_http_get_module_ctx(r->main, ngx_http_zip_module);
sr_ctx = ngx_http_get_module_ctx(r, ngx_http_zip_module);
- if (ctx == NULL || sr_ctx == NULL || in == NULL) {
- return ngx_http_next_body_filter(r, in);
- }
+ if (ctx && sr_ctx && in) {
+ if (sr_ctx->range != NULL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: cutting subrequest to fit range");
+ if ((out = ngx_http_zip_subrequest_range(r, in, sr_ctx)) == NULL)
+ return NGX_OK;
- if (sr_ctx->range != NULL) {
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: cutting subrequest to fit range");
- if ((out = ngx_http_zip_subrequest_range(r, in, sr_ctx)) == NULL) {
- return NGX_OK;
+ return ngx_http_next_body_filter(r, out);
}
- return ngx_http_next_body_filter(r, out);
- }
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: No range for subrequest to satisfy");
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: No range for subrequest to satisfy");
- if (ctx->missing_crc32) {
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: updating CRC-32");
- ngx_http_zip_subrequest_update_crc32(in, sr_ctx->requesting_file);
+ if (sr_ctx->requesting_file->missing_crc32) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: updating CRC-32");
+ ngx_http_zip_subrequest_update_crc32(in, sr_ctx->requesting_file);
+ }
}
-
+
return ngx_http_next_body_filter(r, in);
}
@@ -391,8 +389,9 @@ ngx_http_zip_subrequest_range(ngx_http_request_t *r, ngx_chain_t *in,
sr_ctx->subrequest_pos = last;
- ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: Buffer range %O-%O Request range %O-%O", start, last, range->start, range->end);
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "mod_zip: Buffer range %O-%O Request range %O-%O",
+ start, last, range->start, range->end);
if (range->end <= start || range->start >= last) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -449,9 +448,8 @@ ngx_http_zip_subrequest_update_crc32(ngx_chain_t *in,
size_t len;
u_char *p;
- if (file == NULL) {
+ if (file == NULL)
return NGX_ERROR;
- }
for (cl = in; cl != NULL; cl = cl->next) {
p = cl->buf->pos;
@@ -516,14 +514,6 @@ ngx_http_zip_main_request_body_filter(ngx_http_request_t *r,
return NGX_ERROR;
}
- if (ctx->missing_crc32) {
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: initializing CRC-32");
- if (ngx_http_zip_init_crc32(&ctx->files) == NGX_ERROR) {
- return NGX_ERROR;
- }
- }
-
if (ngx_http_zip_generate_pieces(r, ctx) == NGX_ERROR) {
return NGX_ERROR;
}
@@ -554,26 +544,9 @@ ngx_http_zip_main_request_body_filter(ngx_http_request_t *r,
}
-static ngx_int_t
-ngx_http_zip_init_crc32(ngx_array_t *files)
-{
- ngx_uint_t i;
- ngx_http_zip_file_t *file;
-
- if (files == NULL) {
- return NGX_ERROR;
- }
- for (i=0; i<files->nelts; i++) {
- file = &((ngx_http_zip_file_t *)files->elts)[i];
- file->crc32 = 0xffffffff;
- }
- return NGX_OK;
-}
-
static ngx_int_t
-ngx_http_zip_send_piece(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx,
- ngx_http_zip_piece_t *piece, ngx_http_zip_range_t *range)
+ngx_http_zip_send_piece(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx, ngx_http_zip_piece_t *piece, ngx_http_zip_range_t *range)
{
ngx_chain_t *link;
ngx_http_request_t *sr;
@@ -586,69 +559,65 @@ ngx_http_zip_send_piece(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx,
return ngx_http_next_body_filter(r, link);
}
+ // need to check if the context has something going on....
if (piece->type == zip_file_piece) {
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: subrequest for \"%V?%V\"",
- &piece->file->uri, &piece->file->args);
- if (ctx->wait) {
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: have a wait context for \"%V?%V\"",
- &ctx->wait->uri, &ctx->wait->args);
- if (ctx->wait->done) {
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: wait \"%V?%V\" done",
- &ctx->wait->uri, &ctx->wait->args);
- ctx->wait = NULL;
- } else {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: subrequest for \"%V?%V\"", &piece->file->uri, &piece->file->args);
+ if (ctx->wait) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: wait NOT DONE \"%V?%V\"",
- &ctx->wait->uri, &ctx->wait->args);
- return NGX_AGAIN;
+ "mod_zip: have a wait context for \"%V?%V\"",
+ &ctx->wait->uri, &ctx->wait->args);
+ if (ctx->wait->done) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "mod_zip: wait \"%V?%V\" done",
+ &ctx->wait->uri, &ctx->wait->args);
+ ctx->wait = NULL;
+ } else {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "mod_zip: wait NOT DONE \"%V?%V\"",
+ &ctx->wait->uri, &ctx->wait->args);
+ return NGX_AGAIN;
+ }
}
- }
- rc = ngx_http_subrequest(r, &piece->file->uri,
- &piece->file->args, &sr, NULL, NGX_HTTP_SUBREQUEST_WAITED );
-
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: ngx_http_subrequest returns %d",
- rc);
- if (rc==NGX_ERROR) {
- return NGX_ERROR;
- }
-
- if ((sr_ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_zip_ctx_t))) == NULL) {
+ rc = ngx_http_subrequest(r, &piece->file->uri, &piece->file->args, &sr, NULL, NGX_HTTP_SUBREQUEST_WAITED);
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: subrequest for \"%V?%V\" result %d, allocating some mem on main request's pool", &piece->file->uri, &piece->file->args, rc);
+ if (rc == NGX_ERROR) {
return NGX_ERROR;
}
+
+ if ((sr_ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_zip_ctx_t))) == NULL)
+ return NGX_ERROR;
+
sr_ctx->requesting_file = piece->file;
sr_ctx->subrequest_pos = piece->range.start;
sr_ctx->range = range;
ngx_http_set_ctx(sr, sr_ctx, ngx_http_zip_module);
-
- if (ctx->wait == NULL) {
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: subrequest for \"%V?%V\" result %d", &piece->file->uri, &piece->file->args, rc);
+ if (ctx->wait == NULL) {
ctx->wait = sr;
- return NGX_AGAIN; // must be NGX_AGAIN
- } else {
+ return NGX_AGAIN; // must be NGX_AGAIN
+ } else {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
- "mod_zip : only one subrequest may be waited at the same time; ");
- }
- return NGX_ERROR;
+ "mod_zip : only one subrequest may be waited at the same time; ");
+ }
+ return NGX_ERROR;
}
if (piece->type == zip_trailer_piece) {
- if (ctx->missing_crc32) {
+ if (piece->file->missing_crc32) // should always be true, but if we somehow needed trailer piece - go on
piece->file->crc32 ^= 0xffffffff;
- } else { /* no trailer piece if we already had the CRC-32s */
- return NGX_OK;
- }
- if ((link = ngx_http_zip_data_descriptor_chain_link(r, piece, range)) == NULL)
+
+ if ((link = ngx_http_zip_data_descriptor_chain_link(r, piece, range)) == NULL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: data descriptor failed");
return NGX_ERROR;
+ }
return ngx_http_next_body_filter(r, link);
}
if (piece->type == zip_central_directory_piece) {
- if ((link = ngx_http_zip_central_directory_chain_link(
- r, ctx, piece, range)) == NULL)
+ if ((link = ngx_http_zip_central_directory_chain_link(r, ctx, piece, range)) == NULL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: CD piece failed");
return NGX_ERROR;
+ }
return ngx_http_next_body_filter(r, link);
}
return NGX_ERROR;
@@ -661,17 +630,12 @@ ngx_http_zip_send_boundary(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx,
ngx_chain_t *link;
ngx_buf_t *b;
- if (range->boundary_sent) {
+ if (range->boundary_sent)
return NGX_OK;
- }
- if ((link = ngx_alloc_chain_link(r->pool)) == NULL) {
+ if ((link = ngx_alloc_chain_link(r->pool)) == NULL
+ || (b = ngx_calloc_buf(r->pool)) == NULL)
return NGX_ERROR;
- }
-
- if ((b = ngx_calloc_buf(r->pool)) == NULL) {
- return NGX_ERROR;
- }
b->memory = 1;
b->pos = range->boundary_header.data;
@@ -685,31 +649,23 @@ ngx_http_zip_send_boundary(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx,
}
static ngx_int_t
-ngx_http_zip_send_final_boundary(ngx_http_request_t *r,
- ngx_http_zip_ctx_t *ctx)
+ngx_http_zip_send_final_boundary(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx)
{
size_t len;
ngx_chain_t *link;
ngx_buf_t *b;
- if ((link = ngx_alloc_chain_link(r->pool)) == NULL) {
+ if ((link = ngx_alloc_chain_link(r->pool)) == NULL
+ || (b = ngx_calloc_buf(r->pool)) == NULL)
return NGX_ERROR;
- }
-
- if ((b = ngx_calloc_buf(r->pool)) == NULL) {
- return NGX_ERROR;
- }
len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1;
b->memory = 1;
- b->pos = ngx_palloc(r->pool, len);
- if (b->pos == NULL) {
+ if ((b->pos = ngx_palloc(r->pool, len)) == NULL)
return NGX_ERROR;
- }
-
- b->last = ngx_sprintf(b->pos,
- CRLF "--%0muA--" CRLF, ctx->boundary);
+
+ b->last = ngx_sprintf(b->pos, CRLF "--%0muA--" CRLF, ctx->boundary);
link->buf = b;
link->next = NULL;
@@ -726,14 +682,15 @@ ngx_http_zip_send_pieces(ngx_http_request_t *r,
ngx_http_zip_piece_t *piece;
ngx_http_zip_range_t *range = NULL;
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "mod_zip: sending pieces, starting with piece %d", ctx->pieces_i);
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "mod_zip: sending pieces, starting with piece %d of total %d", ctx->pieces_i, ctx->pieces_n);
switch(ctx->ranges.nelts) {
case 0:
while (rc == NGX_OK && ctx->pieces_i < ctx->pieces_n) {
piece = &ctx->pieces[ctx->pieces_i++];
pieces_sent++;
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: no ranges / sending piece type %d", piece->type);
rc = ngx_http_zip_send_piece(r, ctx, piece, NULL);
}
break;
@@ -743,6 +700,7 @@ ngx_http_zip_send_pieces(ngx_http_request_t *r,
piece = &ctx->pieces[ctx->pieces_i++];
if (ngx_http_zip_ranges_intersect(&piece->range, range)) {
pieces_sent++;
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: 1 range / sending piece type %d", piece->type);
rc = ngx_http_zip_send_piece(r, ctx, piece, range);
}
}
View
42 ngx_http_zip_module.h
@@ -14,15 +14,18 @@ typedef struct {
ngx_uint_t crc32;
ngx_str_t uri;
ngx_str_t args;
- ngx_uint_t index;
+ size_t index; //! zip64 allows for 64bit number of files
ngx_uint_t dos_time;
ngx_uint_t unix_time;
ngx_str_t filename;
- ngx_uint_t size;
- size_t offset;
+ off_t size;
+ off_t offset;
unsigned header_sent:1;
unsigned trailer_sent:1;
+ unsigned missing_crc32:1;
+ unsigned need_zip64:1;
+ unsigned need_zip64_offset:1;
} ngx_http_zip_file_t;
typedef struct {
@@ -33,10 +36,20 @@ typedef struct {
unsigned boundary_sent:1;
} ngx_http_zip_range_t;
+typedef enum {
+ zip_header_piece, //local file header
+ zip_file_piece, // file data
+ zip_trailer_piece, // data descriptor (for files without CRC, exists if bit 3 of GP flag is set),
+ zip_trailer_piece64, // the same but for zip64 (if zip64 extended information extra field is in file header)
+ zip_central_directory_piece,
+ zip_zip64_directory,
+ zip_zip64_directory_locator
+} ngx_http_zip_piece_e;
+
typedef struct {
ngx_http_zip_range_t range;
ngx_http_zip_file_t *file;
- ngx_int_t type;
+ ngx_http_zip_piece_e type;
} ngx_http_zip_piece_t;
typedef struct {
@@ -49,13 +62,13 @@ typedef struct {
ngx_uint_t pieces_n;
ngx_atomic_uint_t boundary;
off_t archive_size;
- ngx_http_request_t *wait; /* the request we're waiting on */
+ off_t cd_size; // zip central directory size
unsigned parsed:1;
unsigned trailer_sent:1;
unsigned abort:1;
- unsigned missing_crc32:1;
- unsigned missing_size:1;
+ unsigned missing_crc32:1; // used in subrequest, if true = reads file into memory and calculates it; also to indicate presence of such file
+ unsigned zip64_used:1;
} ngx_http_zip_ctx_t;
typedef struct {
@@ -64,18 +77,3 @@ typedef struct {
off_t subrequest_pos;
} ngx_http_zip_sr_ctx_t;
-typedef enum {
- zip_start_state = 0,
- zip_filename_state,
- zip_size_state,
- zip_uri_state,
- zip_args_state,
- zip_eol_state
-} ngx_http_zip_state_e;
-
-typedef enum {
- zip_header_piece,
- zip_file_piece,
- zip_trailer_piece,
- zip_central_directory_piece
-} ngx_http_zip_piece_e;
View
95 ngx_http_zip_parsers.c
@@ -1,5 +1,6 @@
+
#line 1 "ngx_http_zip_parsers.rl"
-/* Parser definitions for mod_zip */
+/* Ragel Parser definitions for mod_zip64 */
#include "ngx_http_zip_module.h"
#include "ngx_http_zip_parsers.h"
@@ -21,8 +22,41 @@ ngx_http_zip_file_init(ngx_http_zip_file_t *parsing_file)
parsing_file->crc32 = 0;
parsing_file->size = 0;
+
+ parsing_file->missing_crc32 = 0;
+ parsing_file->need_zip64 = 0;
+ parsing_file->need_zip64_offset = 0;
+}
+
+inline char hex_char_value(unsigned char ch) {
+ if ('0' <= ch && ch <= '9')
+ return ch - '0';
+ if ('A' <= ch && ch <= 'F')
+ return ch - 'A' + 10;
+ if ('a' <= ch && ch <= 'f')
+ return ch - 'A' + 10;
+ return 0;
+}
+
+size_t destructive_url_decode_len(unsigned char* start, unsigned char* end)
+{
+ unsigned char *read_pos = start, *write_pos = start;
+
+ for (; read_pos < end; read_pos++) {
+ unsigned char ch = *read_pos;
+ if (ch == '%' && (read_pos+2 < end)) {
+ ch = 16 * hex_char_value(*(read_pos+1)) + hex_char_value(*(read_pos+2));
+ read_pos += 2;
+ }
+ if (ch == '+')
+ ch = ' ';
+ *(write_pos++) = ch;
+ }
+
+ return write_pos - start;
}
+
static ngx_int_t
ngx_http_zip_clean_range(ngx_http_zip_range_t *range,
int prefix, int suffix, ngx_http_zip_ctx_t *ctx)
@@ -52,7 +86,7 @@ ngx_http_zip_clean_range(ngx_http_zip_range_t *range,
}
-#line 56 "ngx_http_zip_parsers.c"
+#line 90 "ngx_http_zip_parsers.c"
static const char _request_actions[] = {
0, 1, 1, 1, 2, 1, 3, 1,
4, 1, 5, 1, 6, 1, 7, 1,
@@ -115,7 +149,8 @@ static const int request_error = 0;
static const int request_en_main = 1;
-#line 56 "ngx_http_zip_parsers.rl"
+
+#line 89 "ngx_http_zip_parsers.rl"
ngx_int_t
@@ -127,12 +162,12 @@ ngx_http_zip_parse_request(ngx_http_zip_ctx_t *ctx)
ngx_http_zip_file_t *parsing_file = NULL;
-#line 131 "ngx_http_zip_parsers.c"
+#line 166 "ngx_http_zip_parsers.c"
{
cs = request_start;
}
-#line 136 "ngx_http_zip_parsers.c"
+#line 171 "ngx_http_zip_parsers.c"
{
int _klen;
unsigned int _trans;
@@ -207,7 +242,7 @@ ngx_http_zip_parse_request(ngx_http_zip_ctx_t *ctx)
switch ( *_acts++ )
{
case 0:
-#line 68 "ngx_http_zip_parsers.rl"
+#line 101 "ngx_http_zip_parsers.rl"
{
parsing_file = ngx_array_push(&ctx->files);
ngx_http_zip_file_init(parsing_file);
@@ -216,41 +251,44 @@ ngx_http_zip_parse_request(ngx_http_zip_ctx_t *ctx)
}
break;
case 1:
-#line 75 "ngx_http_zip_parsers.rl"
+#line 108 "ngx_http_zip_parsers.rl"
{
parsing_file->uri.data = p;
parsing_file->uri.len = 1;
}
break;
case 2:
-#line 80 "ngx_http_zip_parsers.rl"
+#line 113 "ngx_http_zip_parsers.rl"
{
- parsing_file->uri.len = p - parsing_file->uri.data;
+ //parsing_file->uri.len = fpc - parsing_file->uri.data;
+ parsing_file->uri.len = destructive_url_decode_len(parsing_file->uri.data, p);
}
break;
case 3:
-#line 83 "ngx_http_zip_parsers.rl"
+#line 117 "ngx_http_zip_parsers.rl"
{
parsing_file->args.data = p;
}
break;
case 4:
-#line 86 "ngx_http_zip_parsers.rl"
+#line 120 "ngx_http_zip_parsers.rl"
{
parsing_file->args.len = p - parsing_file->args.data;
}
break;
case 5:
-#line 89 "ngx_http_zip_parsers.rl"
+#line 123 "ngx_http_zip_parsers.rl"
{
parsing_file->size = parsing_file->size * 10 + ((*p) - '0');
}
break;
case 6:
-#line 92 "ngx_http_zip_parsers.rl"
+#line 126 "ngx_http_zip_parsers.rl"
{
if ((*p) == '-') {
ctx->missing_crc32 = 1;
+ parsing_file->missing_crc32 = 1;
+ parsing_file->crc32 = 0xffffffff;
} else {
parsing_file->crc32 *= 16;
if ((*p) >= 'a' && (*p) <= 'f') {
@@ -265,18 +303,18 @@ ngx_http_zip_parse_request(ngx_http_zip_ctx_t *ctx)
}
break;
case 7:
-#line 107 "ngx_http_zip_parsers.rl"
+#line 143 "ngx_http_zip_parsers.rl"
{
parsing_file->filename.data = p;
}
break;
case 8:
-#line 110 "ngx_http_zip_parsers.rl"
+#line 146 "ngx_http_zip_parsers.rl"
{
parsing_file->filename.len = p - parsing_file->filename.data;
}
break;
-#line 280 "ngx_http_zip_parsers.c"
+#line 318 "ngx_http_zip_parsers.c"
}
}
@@ -288,7 +326,8 @@ ngx_http_zip_parse_request(ngx_http_zip_ctx_t *ctx)
_test_eof: {}
_out: {}
}
-#line 129 "ngx_http_zip_parsers.rl"
+
+#line 165 "ngx_http_zip_parsers.rl"
if (cs < request_first_final) {
@@ -301,7 +340,7 @@ ngx_http_zip_parse_request(ngx_http_zip_ctx_t *ctx)
}
-#line 305 "ngx_http_zip_parsers.c"
+#line 344 "ngx_http_zip_parsers.c"
static const char _range_actions[] = {
0, 1, 0, 1, 1, 1, 2, 2,
0, 1, 2, 3, 1
@@ -353,7 +392,8 @@ static const int range_error = 0;
static const int range_en_main = 1;
-#line 143 "ngx_http_zip_parsers.rl"
+
+#line 179 "ngx_http_zip_parsers.rl"
ngx_int_t
@@ -366,12 +406,12 @@ ngx_http_zip_parse_range(ngx_http_request_t *r, ngx_str_t *range_str, ngx_http_z
u_char *pe = range_str->data + range_str->len;
-#line 370 "ngx_http_zip_parsers.c"
+#line 410 "ngx_http_zip_parsers.c"
{
cs = range_start;
}
-#line 375 "ngx_http_zip_parsers.c"
+#line 415 "ngx_http_zip_parsers.c"
{
int _klen;
unsigned int _trans;
@@ -445,7 +485,7 @@ ngx_http_zip_parse_range(ngx_http_request_t *r, ngx_str_t *range_str, ngx_http_z
switch ( *_acts++ )
{
case 0:
-#line 155 "ngx_http_zip_parsers.rl"
+#line 191 "ngx_http_zip_parsers.rl"
{
if (range) {
if (ngx_http_zip_clean_range(range, prefix, suffix, ctx) == NGX_ERROR) {
@@ -461,18 +501,18 @@ ngx_http_zip_parse_range(ngx_http_request_t *r, ngx_str_t *range_str, ngx_http_z
}
break;
case 1:
-#line 169 "ngx_http_zip_parsers.rl"
+#line 205 "ngx_http_zip_parsers.rl"
{ range->start = range->start * 10 + ((*p) - '0'); }
break;
case 2:
-#line 171 "ngx_http_zip_parsers.rl"
+#line 207 "ngx_http_zip_parsers.rl"
{ range->end = range->end * 10 + ((*p) - '0'); prefix = 0; }
break;
case 3:
-#line 173 "ngx_http_zip_parsers.rl"
+#line 209 "ngx_http_zip_parsers.rl"
{ suffix = 1; }
break;
-#line 476 "ngx_http_zip_parsers.c"
+#line 516 "ngx_http_zip_parsers.c"
}
}
@@ -484,7 +524,8 @@ ngx_http_zip_parse_range(ngx_http_request_t *r, ngx_str_t *range_str, ngx_http_z
_test_eof: {}
_out: {}
}
-#line 186 "ngx_http_zip_parsers.rl"
+
+#line 222 "ngx_http_zip_parsers.rl"
if (cs < range_first_final) {
View
40 ngx_http_zip_parsers.rl
@@ -1,4 +1,4 @@
-/* Parser definitions for mod_zip */
+/* Ragel Parser definitions for mod_zip64 */
#include "ngx_http_zip_module.h"
#include "ngx_http_zip_parsers.h"
@@ -20,8 +20,41 @@ ngx_http_zip_file_init(ngx_http_zip_file_t *parsing_file)
parsing_file->crc32 = 0;
parsing_file->size = 0;
+
+ parsing_file->missing_crc32 = 0;
+ parsing_file->need_zip64 = 0;
+ parsing_file->need_zip64_offset = 0;
+}
+
+inline char hex_char_value(unsigned char ch) {
+ if ('0' <= ch && ch <= '9')
+ return ch - '0';
+ if ('A' <= ch && ch <= 'F')
+ return ch - 'A' + 10;
+ if ('a' <= ch && ch <= 'f')
+ return ch - 'A' + 10;
+ return 0;
+}
+
+size_t destructive_url_decode_len(unsigned char* start, unsigned char* end)
+{
+ unsigned char *read_pos = start, *write_pos = start;
+
+ for (; read_pos < end; read_pos++) {
+ unsigned char ch = *read_pos;
+ if (ch == '%' && (read_pos+2 < end)) {
+ ch = 16 * hex_char_value(*(read_pos+1)) + hex_char_value(*(read_pos+2));
+ read_pos += 2;
+ }
+ if (ch == '+')
+ ch = ' ';
+ *(write_pos++) = ch;
+ }
+
+ return write_pos - start;
}
+
static ngx_int_t
ngx_http_zip_clean_range(ngx_http_zip_range_t *range,
int prefix, int suffix, ngx_http_zip_ctx_t *ctx)
@@ -78,7 +111,8 @@ ngx_http_zip_parse_request(ngx_http_zip_ctx_t *ctx)
}
action end_uri {
- parsing_file->uri.len = fpc - parsing_file->uri.data;
+ //parsing_file->uri.len = fpc - parsing_file->uri.data;
+ parsing_file->uri.len = destructive_url_decode_len(parsing_file->uri.data, fpc);
}
action start_args {
parsing_file->args.data = fpc;
@@ -92,6 +126,8 @@ ngx_http_zip_parse_request(ngx_http_zip_ctx_t *ctx)
action crc_incr {
if (fc == '-') {
ctx->missing_crc32 = 1;
+ parsing_file->missing_crc32 = 1;
+ parsing_file->crc32 = 0xffffffff;
} else {
parsing_file->crc32 *= 16;
if (fc >= 'a' && fc <= 'f') {
View
5 t/nginx.conf
@@ -47,7 +47,9 @@ http {
}
location /zip {
- add_header X-Archive-Files zip;
+ add_header X-Archive-Files zip;
+ add_header Last-Modified "Wed, 15 Nov 1995 04:58:08 GMT";
+ add_header ETag "3.14159";
}
}
@@ -68,7 +70,6 @@ http {
location /local {
alias html;
- index index.html index.htm;
}
#error_page 404 /404.html;
View
1  t/nginx/html/file1 with spaces.txt
@@ -0,0 +1 @@
+This is the first file.
View
2  t/nginx/html/zip-spaces.txt
@@ -0,0 +1,2 @@
+1a6349c5 24 /file1%20with%20spaces.txt file1.txt
+5d70c4d3 25 /file2.txt file2.txt
View
82 t/ziptest.pl
@@ -1,13 +1,41 @@
#!/usr/bin/perl
-use Test::More tests => 75;
+# TODO tests for Zip64
+
+use Test::More tests => 84;
use LWP::UserAgent;
use Archive::Zip;
$temp_zip_path = "/tmp/mod_zip.zip";
$http_root = "http://localhost:8081";
-sub read_file {
+sub set_debug_log($) {
+ my $label = shift;
+ $/ = "\n";
+ open( NEWCONF, ">", "nginx/conf/nginx.conf" );
+
+ open( CONF, "<", "nginx.conf" );
+ while(my $line = <CONF>) {
+ if ($line eq "error_log logs/error.log debug;\n") {
+ print NEWCONF "error_log logs/error-$label.log debug;\n";
+ } else {
+ print NEWCONF $line;
+ }
+ }
+ close( CONF );
+
+ close( NEWCONF );
+
+ my $pid = `cat nginx/logs/nginx.pid`;
+ if ($?) {
+ print "Starting nginx...\n";
+ `nginx/sbin/nginx`;
+ }
+ `/bin/kill -HUP $pid`;
+ sleep 1;
+}
+
+sub read_file($) {
$file = shift;
undef $/;
@@ -18,7 +46,7 @@ sub read_file {
return $contents;
}
-sub write_temp_zip {
+sub write_temp_zip($) {
my $content = shift;
open TEMPFILE, ">", $temp_zip_path;
@@ -28,7 +56,7 @@ sub write_temp_zip {
return Archive::Zip->new($temp_zip_path);
}
-sub test_zip_archive {
+sub test_zip_archive($$) {
my ($content, $condition) = @_;
my $zip = write_temp_zip($content);
@@ -58,6 +86,8 @@ sub test_zip_archive {
########## Dowload component files
+set_debug_log("component");
+
$response = $ua->get("$http_root/file1.txt");
is( $response->content, $file1_content, "download file1.txt" );
@@ -72,10 +102,12 @@ sub test_zip_archive {
########## Download ZIP files
+set_debug_log("basic-zip");
+
$response = $ua->get("$http_root/zip-missing-crc.txt");
is($response->code, 200, "Returns OK with missing CRC");
like($response->header("Content-Length"), qr/^\d+$/, "Content-Length header when missing CRC");
-is($response->header("Accept-Ranges"), undef, "No Accept-Ranges header when missing CRC");
+is($response->header("Accept-Ranges"), undef, "No Accept-Ranges header when missing CRC (fails with nginx 0.7.44 - 0.8.6)");
$zip = test_zip_archive($response->content, "when missing CRC");
is($zip->memberNamed("file1.txt")->hasDataDescriptor(), 8, "Has data descriptor when missing CRC");
@@ -99,6 +131,12 @@ sub test_zip_archive {
is($zip->memberNamed("file1.txt")->crc32String(), "1a6349c5", "Generated file1.txt CRC is correct (local)");
is($zip->memberNamed("file2.txt")->crc32String(), "5d70c4d3", "Generated file2.txt CRC is correct (local)");
+$response = $ua->get("$http_root/zip-spaces.txt");
+is($response->code, 200, "Returns OK with spaces in URLs");
+
+$zip = test_zip_archive($response->content, "with spaces in URLs");
+is($zip->numberOfMembers(), 2, "Correct number in spaced-out ZIP");
+
open LARGEFILE, ">", "nginx/html/largefile.txt";
for (0..99999) {
print LARGEFILE "X" x 99;
@@ -106,6 +144,8 @@ sub test_zip_archive {
}
close LARGEFILE;
+set_debug_log("zip-large");
+
$response = $ua->get("$http_root/zip-large-file.txt");
is($response->code, 200, "Returns OK with large file");
@@ -116,6 +156,8 @@ sub test_zip_archive {
unlink "nginx/html/largefile.txt";
+set_debug_log("zip-headers");
+
$response = $ua->get("$http_root/zip.txt");
is( $response->header( "X-Archive-Files" ), "zip",
"X-Archive-Files header" );
@@ -127,12 +169,16 @@ sub test_zip_archive {
"Accept-Ranges header" );
is( $response->header( "Content-Length" ), $zip_length,
"Content-Length header" );
+is( $response->header( "Last-Modified" ), "Wed, 15 Nov 1995 04:58:08 GMT",
+ "Last-Modified header" );
-$zip = test_zip_archive($response->content);
+$zip = test_zip_archive($response->content, "with CRC");
is($zip->memberNamed("file1.txt")->hasDataDescriptor(), 0, "No data descriptor when CRC supplied");
########### Byte-range
+set_debug_log("range");
+
$response = $ua->get("$http_root/zip.txt", "Range" => "bytes=10000-10001");
is($response->code, 416, "Request range not satisfiable");
@@ -230,3 +276,27 @@ sub test_zip_archive {
qr(--$boundary\r\nContent-Type: application/zip\r\nContent-Range: bytes 0-1/$zip_length\r\n\r\nPK\r\n),
"Second chunk");
+### If-Range ###
+set_debug_log("if-range");
+
+# last-modified
+$response = $ua->get("$http_root/zip.txt",
+ "If-Range" => "Wed, 15 Nov 1995 04:58:07 GMT",
+ "Range" => "bytes=0-1");
+is($response->code, 200, "200 OK -- when If-Range is not Last-Modified time");
+
+$response = $ua->get("$http_root/zip.txt",
+ "If-Range" => "Wed, 15 Nov 1995 04:58:08 GMT",
+ "Range" => "bytes=0-1");
+is($response->code, 206, "206 Partial Content -- when If-Range is Last-Modified time");
+
+# etag
+$response = $ua->get("$http_root/zip.txt",
+ "If-Range" => "2.71828",
+ "Range" => "bytes=0-1");
+is($response->code, 200, "200 OK -- when If-Range is not ETag");
+
+$response = $ua->get("$http_root/zip.txt",
+ "If-Range" => "3.14159",
+ "Range" => "bytes=0-1");
+is($response->code, 206, "206 Partial Content -- when If-Range is ETag (requires nginx 0.8.10+ or nginx-0.8.9-etag.patch)");
Please sign in to comment.
Something went wrong with that request. Please try again.