Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 771 lines (634 sloc) 24.108 kb
72f60ce First commit.
Evan Miller authored
1 /*
2 * mod_zip
3 *
4 * Copyright (C) Evan Miller
5 *
6 * This module may be distributed under the same terms as Nginx itself.
7 */
8
9 #include "ngx_http_zip_module.h"
10 #include "ngx_http_zip_parsers.h"
11 #include "ngx_http_zip_file.h"
12 #include "ngx_http_zip_headers.h"
13
14 static size_t ngx_chain_length(ngx_chain_t *chain_link);
15 static ngx_chain_t *ngx_chain_last_link(ngx_chain_t *chain_link);
16 static ngx_int_t ngx_http_zip_discard_chain(ngx_http_request_t *r,
17 ngx_chain_t *in);
18
19 static ngx_int_t ngx_http_zip_ranges_intersect(ngx_http_zip_range_t *range1,
20 ngx_http_zip_range_t *range2);
21
22 static ngx_int_t ngx_http_zip_copy_unparsed_request(ngx_http_request_t *r,
23 ngx_chain_t *in, ngx_http_zip_ctx_t *ctx);
24 static ngx_int_t ngx_http_zip_set_headers(ngx_http_request_t *r,
25 ngx_http_zip_ctx_t *ctx);
26
27 static ngx_int_t ngx_http_zip_header_filter(ngx_http_request_t *r);
28 static ngx_int_t ngx_http_zip_body_filter(ngx_http_request_t *r,
29 ngx_chain_t *in);
30 static ngx_int_t ngx_http_zip_main_request_body_filter(ngx_http_request_t *r,
31 ngx_chain_t *in);
32 static ngx_int_t ngx_http_zip_subrequest_body_filter(ngx_http_request_t *r,
33 ngx_chain_t *in);
34
35 static ngx_chain_t* ngx_http_zip_subrequest_range(ngx_http_request_t *r, ngx_chain_t *in,
36 ngx_http_zip_sr_ctx_t *sr_ctx);
37 static ngx_int_t ngx_http_zip_subrequest_update_crc32(ngx_chain_t *in,
38 ngx_http_zip_file_t *file);
39
40 static ngx_int_t ngx_http_zip_send_pieces(ngx_http_request_t *r,
41 ngx_http_zip_ctx_t *ctx);
42 static ngx_int_t ngx_http_zip_send_piece(ngx_http_request_t *r,
43 ngx_http_zip_ctx_t *ctx, ngx_http_zip_piece_t *piece,
44 ngx_http_zip_range_t *range);
45
46 static ngx_int_t ngx_http_zip_send_boundary(ngx_http_request_t *r,
47 ngx_http_zip_ctx_t *ctx, ngx_http_zip_range_t *range);
48 static ngx_int_t ngx_http_zip_send_final_boundary(ngx_http_request_t *r,
49 ngx_http_zip_ctx_t *ctx);
50
51 static ngx_int_t ngx_http_zip_init(ngx_conf_t *cf);
52 static ngx_int_t ngx_http_zip_init_crc32(ngx_array_t *files);
53
54 static ngx_int_t ngx_http_zip_main_request_header_filter(ngx_http_request_t *r);
55 static ngx_int_t ngx_http_zip_subrequest_header_filter(ngx_http_request_t *r);
56
57 static ngx_str_t ngx_http_zip_header_variable_name = ngx_string("upstream_http_x_archive_files");
58
59 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
60 static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
61
62 static ngx_http_module_t ngx_http_zip_module_ctx = {
63 NULL, /* preconfiguration */
64 ngx_http_zip_init, /* postconfiguration */
65
66 NULL, /* create main configuration */
67 NULL, /* init main configuration */
68
69 NULL, /* create server configuration */
70 NULL, /* merge server configuration */
71
72 NULL, /* create location configuration */
73 NULL /* merge location configuration */
74 };
75
76 ngx_module_t ngx_http_zip_module = {
77 NGX_MODULE_V1,
78 &ngx_http_zip_module_ctx, /* module context */
79 NULL, /* module directives */
80 NGX_HTTP_MODULE, /* module type */
81 NULL, /* init master */
82 NULL, /* init module */
83 NULL, /* init process */
84 NULL, /* init thread */
85 NULL, /* exit thread */
86 NULL, /* exit process */
87 NULL, /* exit master */
88 NGX_MODULE_V1_PADDING
89 };
90
91 static size_t ngx_chain_length(ngx_chain_t *chain_link)
92 {
93 size_t len;
94 for (len=0; chain_link; chain_link = chain_link->next) {
95 len += chain_link->buf->last - chain_link->buf->pos;
96 }
97 return len;
98 }
99
100 static ngx_chain_t *ngx_chain_last_link(ngx_chain_t *chain_link)
101 {
102 ngx_chain_t *cl;
103 for (cl = chain_link; cl->next; cl = cl->next) {
104 /* void */
105 }
106 return cl;
107 }
108
109 static ngx_int_t
110 ngx_http_zip_discard_chain(ngx_http_request_t *r,
111 ngx_chain_t *in)
112 {
113 ngx_chain_t *chain_link;
114
115 for (chain_link = in; chain_link; chain_link = chain_link->next) {
116 chain_link->buf->flush = 1;
117 chain_link->buf->sync = 1;
118 chain_link->buf->temporary = 0;
119 chain_link->buf->memory = 0;
120 chain_link->buf->mmap = 0;
121 chain_link->buf->last = chain_link->buf->pos;
122 }
123
124 return ngx_http_next_body_filter(r, in);
125 }
126
127 static ngx_int_t
128 ngx_http_zip_ranges_intersect(ngx_http_zip_range_t *range1, ngx_http_zip_range_t *range2)
129 {
130 return (range1 == NULL) || (range2 == NULL) ||
131 !(range1->start >= range2->end || range2->start >= range1->end);
132 }
133
134 static ngx_int_t ngx_http_zip_copy_unparsed_request(ngx_http_request_t *r,
135 ngx_chain_t *in, ngx_http_zip_ctx_t *ctx)
136 {
137 ngx_str_t *old_unparsed_request;
138 ngx_chain_t *chain_link;
139 size_t len, offset = 0;
140
141 old_unparsed_request = ctx->unparsed_request;
142
143 len = ngx_chain_length(in);
144
145 if (old_unparsed_request != NULL) {
146 len += old_unparsed_request->len;
147 }
148
149 if ((ctx->unparsed_request = ngx_palloc(r->pool, sizeof(ngx_str_t))) == NULL) {
150 return NGX_ERROR;
151 }
152
153 if ((ctx->unparsed_request->data = ngx_palloc(r->pool, len)) == NULL) {
154 return NGX_ERROR;
155 }
156
157 if (old_unparsed_request != NULL) {
158 ngx_memcpy(ctx->unparsed_request->data, old_unparsed_request->data, old_unparsed_request->len);
159 offset += old_unparsed_request->len;
160 }
161
162 for (chain_link = in; chain_link; chain_link = chain_link->next ) {
163 ngx_memcpy(ctx->unparsed_request->data + offset, chain_link->buf->pos,
164 chain_link->buf->last - chain_link->buf->pos);
165 offset += chain_link->buf->last - chain_link->buf->pos;
166 }
167
168 ctx->unparsed_request->len = offset;
169
170 chain_link = ngx_chain_last_link(in);
171
172 return chain_link->buf->last_buf ? NGX_OK: NGX_AGAIN;
173 }
174
175 /*
176 * The header filter looks for "X-Archive-Files: zip" and allocates
177 * a module context struct if found
178 */
179 static ngx_int_t ngx_http_zip_header_filter(ngx_http_request_t *r)
180 {
181 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
182 "mod_zip: entering header filter");
183
184 if (r != r->main) {
185 return ngx_http_zip_subrequest_header_filter(r);
186 }
187
188 return ngx_http_zip_main_request_header_filter(r);
189 }
190
191 static ngx_int_t
192 ngx_http_zip_main_request_header_filter(ngx_http_request_t *r)
193 {
194 ngx_http_variable_value_t *vv;
195 ngx_http_zip_ctx_t *ctx;
196
197 if ((ctx = ngx_http_get_module_ctx(r, ngx_http_zip_module)) != NULL) {
198 return ngx_http_next_header_filter(r);
199 }
200
201 if ((vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t))) == NULL) {
202 return NGX_ERROR;
203 }
204
205 /* Look for X-Archive-Files */
206 if (ngx_http_upstream_header_variable(r, vv,
207 (uintptr_t)(&ngx_http_zip_header_variable_name)) != NGX_OK
208 || vv->not_found
209 || ngx_strncmp(vv->data, "zip", sizeof("zip") - 1) != 0)
210 {
211 return ngx_http_next_header_filter(r);
212 }
213
214 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
215 "mod_zip: X-Archive-Files found");
216
217 if ((ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_zip_ctx_t))) == NULL) {
218 return NGX_ERROR;
219 }
220
221 if (ngx_array_init(&ctx->files, r->pool, 1, sizeof(ngx_http_zip_file_t))
222 == NGX_ERROR)
223 {
224 return NGX_ERROR;
225 }
226
227 if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_zip_range_t))
228 == NGX_ERROR)
229 {
230 return NGX_ERROR;
231 }
232
233 ngx_http_set_ctx(r, ctx, ngx_http_zip_module);
234
235 return NGX_OK;
236 }
237
238 static ngx_int_t
239 ngx_http_zip_subrequest_header_filter(ngx_http_request_t *r)
240 {
241 ngx_http_zip_ctx_t *ctx;
242
243 ctx = ngx_http_get_module_ctx(r->main, ngx_http_zip_module);
244 if (ctx != NULL) {
245 if (r->headers_out.status != NGX_HTTP_OK
246 && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) {
247 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
248 "mod_zip: a subrequest returned %d, aborting...",
249 r->headers_out.status);
250 ctx->abort = 1;
251 return NGX_ERROR;
252 }
253 if (ctx->missing_crc32) {
254 r->filter_need_in_memory = 1;
255 }
256 }
257 return ngx_http_next_header_filter(r);
258 }
259
260 static ngx_int_t
261 ngx_http_zip_set_headers(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx)
262 {
263 if (ngx_http_zip_add_cache_control(r) == NGX_ERROR) {
264 return NGX_ERROR;
265 }
266
267 r->headers_out.content_type.len = sizeof(NGX_ZIP_MIME_TYPE) - 1;
268 r->headers_out.content_type.data = (u_char *)NGX_ZIP_MIME_TYPE;
269 ngx_http_clear_content_length(r);
270 ngx_http_clear_last_modified(r);
271
272 if (ctx->missing_crc32) {
273 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
274 "mod_zip: Clearing Accept-Ranges header");
275 ngx_http_clear_accept_ranges(r);
276 }
277 r->headers_out.content_length_n = ctx->archive_size;
278 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
279 "mod_zip: Archive will be %O bytes", ctx->archive_size);
280 if (r->headers_in.range && !r->headers_in.if_range) {
281 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
282 "mod_zip: Range found");
283 if (ctx->missing_crc32) {
284 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
285 "mod_zip: Missing checksums, ignoring Range");
286 return NGX_OK;
287 }
288 if (ngx_http_zip_parse_range(r, &r->headers_in.range->value, ctx)
289 == NGX_ERROR) {
290 r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
291 if (ngx_http_zip_add_full_content_range(r) == NGX_ERROR) {
292 return NGX_ERROR;
293 }
294 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
295 "mod_zip: Range not satisfiable");
296 ctx->ranges.nelts = 0;
297 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
298 }
299 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
300 "mod_zip: Range is satisfiable");
301 if (ctx->ranges.nelts == 1) {
302 if (ngx_http_zip_add_partial_content_range(r, ctx) == NGX_ERROR) {
303 return NGX_ERROR;
304 }
305 } else {
306 if (ngx_http_zip_init_multipart_range(r, ctx) == NGX_ERROR) {
307 return NGX_ERROR;
308 }
309 }
310 r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
311 r->headers_out.status_line.len = 0;
312 }
313
314 return NGX_OK;
315 }
316
317 static ngx_int_t
318 ngx_http_zip_body_filter(ngx_http_request_t *r,
319 ngx_chain_t *in)
320 {
321 if (r != r->main) {
322 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
323 "mod_zip: entering subrequest body filter");
324 return ngx_http_zip_subrequest_body_filter(r, in);
325 }
326
327 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
328 "mod_zip: entering main request body filter");
329 return ngx_http_zip_main_request_body_filter(r, in);
330 }
331
332 static ngx_int_t
333 ngx_http_zip_subrequest_body_filter(ngx_http_request_t *r,
334 ngx_chain_t *in)
335 {
336 ngx_http_zip_ctx_t *ctx;
337 ngx_http_zip_sr_ctx_t *sr_ctx;
338 ngx_chain_t *out;
339
340 ctx = ngx_http_get_module_ctx(r->main, ngx_http_zip_module);
341 sr_ctx = ngx_http_get_module_ctx(r, ngx_http_zip_module);
342
343 if (ctx == NULL || sr_ctx == NULL || in == NULL) {
344 return ngx_http_next_body_filter(r, in);
345 }
346
347 if (sr_ctx->range != NULL) {
348 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
349 "mod_zip: cutting subrequest to fit range");
350 if ((out = ngx_http_zip_subrequest_range(r, in, sr_ctx)) == NULL) {
351 return NGX_OK;
352 }
353 return ngx_http_next_body_filter(r, out);
354 }
355
356 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
357 "mod_zip: No range for subrequest to satisfy");
358
359 if (ctx->missing_crc32) {
360 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
361 "mod_zip: updating CRC-32");
362 ngx_http_zip_subrequest_update_crc32(in, sr_ctx->requesting_file);
363 }
364
365 return ngx_http_next_body_filter(r, in);
366 }
367
368 /* more or less copied from ngx_http_range_filter_module.c */
369 static ngx_chain_t*
370 ngx_http_zip_subrequest_range(ngx_http_request_t *r, ngx_chain_t *in,
371 ngx_http_zip_sr_ctx_t *sr_ctx)
372 {
373 ngx_chain_t *out, *cl, **ll;
374 ngx_buf_t *buf;
375 ngx_http_zip_range_t *range;
376 off_t start, last;
377
378 range = sr_ctx->range;
379
380 out = NULL;
381 ll = &out;
382
383 for (cl = in; cl != NULL; cl = cl->next) {
384 buf = cl->buf;
385
386 start = sr_ctx->subrequest_pos;
387 last = sr_ctx->subrequest_pos + ngx_buf_size(buf);
388
389 sr_ctx->subrequest_pos = last;
390
391 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
392 "mod_zip: Buffer range %O-%O Request range %O-%O", start, last, range->start, range->end);
393
394 if (range->end <= start || range->start >= last) {
395 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
396 "mod_zip: range body skip");
397
398 if (buf->in_file) {
399 buf->file_pos = buf->file_last;
400 }
401
402 buf->pos = buf->last;
403 buf->sync = 1;
404
405 continue;
406 }
407
408 if (range->start > start) {
409 if (buf->in_file) {
410 buf->file_pos += range->start - start;
411 }
412
413 if (ngx_buf_in_memory(buf)) {
414 buf->pos += (size_t) (range->start - start);
415 }
416 }
417
418 if (range->end <= last) {
419 if (buf->in_file) {
420 buf->file_last -= last - range->end;
421 }
422
423 if (ngx_buf_in_memory(buf)) {
424 buf->last -= (size_t) (last - range->end);
425 }
426
427 buf->last_buf = 1;
428 *ll = cl;
429 cl->next = NULL;
430
431 break;
432 }
433
434 *ll = cl;
435 ll = &cl->next;
436 }
437
438 return out;
439 }
440
441 static ngx_int_t
442 ngx_http_zip_subrequest_update_crc32(ngx_chain_t *in,
443 ngx_http_zip_file_t *file)
444 {
445 ngx_chain_t *cl;
446 size_t len;
447 u_char *p;
448
449 if (file == NULL) {
450 return NGX_ERROR;
451 }
452
453 for (cl = in; cl != NULL; cl = cl->next) {
454 p = cl->buf->pos;
455 len = cl->buf->last - p;
456 while(len--) {
457 file->crc32 = ngx_crc32_table256[(file->crc32 ^ *p++) & 0xff]
458 ^ (file->crc32 >> 8);
459 }
460 }
461
462 return NGX_OK;
463 }
464
465 static ngx_int_t
466 ngx_http_zip_main_request_body_filter(ngx_http_request_t *r,
467 ngx_chain_t *in)
468 {
469 ngx_http_zip_ctx_t *ctx;
470 ngx_chain_t *chain_link;
471 int rc;
472
473 ctx = ngx_http_get_module_ctx(r, ngx_http_zip_module);
474
475 if (ctx == NULL || ctx->trailer_sent) {
476 return ngx_http_next_body_filter(r, in);
477 }
478
479 if (ctx->abort) {
480 return NGX_ERROR;
481 }
482
483 if (r->headers_out.status != NGX_HTTP_OK &&
484 r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) {
485 return ngx_http_next_body_filter(r, in);
486 }
487
488 if (ctx->parsed) {
489 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
490 "mod_zip: restarting subrequests");
491 return ngx_http_zip_send_pieces(r, ctx);
492 }
493
494 if (in == NULL) {
495 return ngx_http_next_body_filter(r, NULL);
496 }
497
498 rc = ngx_http_zip_copy_unparsed_request(r, in, ctx);
499 if (rc == NGX_AGAIN) {
500 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
501 "mod_zip: not the last buf");
502 return ngx_http_zip_discard_chain(r, in);
503 } else if (rc == NGX_ERROR) {
504 return NGX_ERROR;
505 }
506
507 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
508 "mod_zip: about to parse list");
509
510 if (ngx_http_zip_parse_request(ctx) == NGX_ERROR) {
511 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
512 "mod_zip: invalid file list from upstream");
513 return NGX_ERROR;
514 }
515
516 if (ctx->missing_crc32) {
517 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
518 "mod_zip: initializing CRC-32");
519 if (ngx_http_zip_init_crc32(&ctx->files) == NGX_ERROR) {
520 return NGX_ERROR;
521 }
522 }
523
524 if (ngx_http_zip_generate_pieces(r, ctx) == NGX_ERROR) {
525 return NGX_ERROR;
526 }
527
528 if (!r->header_sent) {
529 rc = ngx_http_zip_set_headers(r, ctx);
530 if (rc == NGX_ERROR) {
531 return NGX_ERROR;
532 }
533 if (rc == NGX_HTTP_RANGE_NOT_SATISFIABLE) {
534 return ngx_http_special_response_handler(r, rc);
535 }
536 if ((rc = ngx_http_send_header(r)) != NGX_OK) {
537 return rc;
538 }
539 }
540
541 chain_link = ngx_chain_last_link(in);
542 chain_link->buf->last_buf = 0;
543
544 if (ngx_http_zip_strip_range_header(r) == NGX_ERROR) {
545 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
546 "mod_zip: failed to strip Range: header from request");
547 return NGX_ERROR;
548 }
549
550 return ngx_http_zip_send_pieces(r, ctx);
551 }
552
553
554 static ngx_int_t
555 ngx_http_zip_init_crc32(ngx_array_t *files)
556 {
557 ngx_uint_t i;
558 ngx_http_zip_file_t *file;
559
560 if (files == NULL) {
561 return NGX_ERROR;
562 }
563 for (i=0; i<files->nelts; i++) {
564 file = &((ngx_http_zip_file_t *)files->elts)[i];
565 file->crc32 = 0xffffffff;
566 }
567 return NGX_OK;
568 }
569
570
571 static ngx_int_t
572 ngx_http_zip_send_piece(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx,
573 ngx_http_zip_piece_t *piece, ngx_http_zip_range_t *range)
574 {
575 ngx_chain_t *link;
576 ngx_http_request_t *sr;
577 ngx_http_zip_sr_ctx_t *sr_ctx;
578 ngx_int_t rc;
579
580 if (piece->type == zip_header_piece) {
581 if ((link = ngx_http_zip_file_header_chain_link(r, ctx, piece, range)) == NULL)
582 return NGX_ERROR;
583 return ngx_http_next_body_filter(r, link);
584 }
585
586 if (piece->type == zip_file_piece) {
587 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
588 "mod_zip: subrequest for \"%V?%V\"",
589 &piece->file->uri, &piece->file->args);
590 rc = ngx_http_subrequest(r, &piece->file->uri,
591 &piece->file->args, &sr, NULL, 0);
592 if ((sr_ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_zip_ctx_t))) == NULL) {
593 return NGX_ERROR;
594 }
595 sr_ctx->requesting_file = piece->file;
596 sr_ctx->subrequest_pos = piece->range.start;
597 sr_ctx->range = range;
598 ngx_http_set_ctx(sr, sr_ctx, ngx_http_zip_module);
599 return rc;
600 }
601
602 if (piece->type == zip_trailer_piece) {
603 if (ctx->missing_crc32) {
604 piece->file->crc32 ^= 0xffffffff;
605 } else { /* no trailer piece if we already had the CRC-32s */
606 return NGX_OK;
607 }
608 if ((link = ngx_http_zip_data_descriptor_chain_link(r, piece, range)) == NULL)
609 return NGX_ERROR;
610 return ngx_http_next_body_filter(r, link);
611 }
612
613 if (piece->type == zip_central_directory_piece) {
614 if ((link = ngx_http_zip_central_directory_chain_link(
615 r, ctx, piece, range)) == NULL)
616 return NGX_ERROR;
617 return ngx_http_next_body_filter(r, link);
618 }
619 return NGX_ERROR;
620 }
621
622 static ngx_int_t
623 ngx_http_zip_send_boundary(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx,
624 ngx_http_zip_range_t *range)
625 {
626 ngx_chain_t *link;
627 ngx_buf_t *b;
628
629 if (range->boundary_sent) {
630 return NGX_OK;
631 }
632
633 if ((link = ngx_alloc_chain_link(r->pool)) == NULL) {
634 return NGX_ERROR;
635 }
636
637 if ((b = ngx_calloc_buf(r->pool)) == NULL) {
638 return NGX_ERROR;
639 }
640
641 b->memory = 1;
642 b->pos = range->boundary_header.data;
643 b->last = b->pos + range->boundary_header.len;
644
645 link->buf = b;
646 link->next = NULL;
647
648 range->boundary_sent = 1;
649 return ngx_http_next_body_filter(r, link);
650 }
651
652 static ngx_int_t
653 ngx_http_zip_send_final_boundary(ngx_http_request_t *r,
654 ngx_http_zip_ctx_t *ctx)
655 {
656 size_t len;
657 ngx_chain_t *link;
658 ngx_buf_t *b;
659
660 if ((link = ngx_alloc_chain_link(r->pool)) == NULL) {
661 return NGX_ERROR;
662 }
663
664 if ((b = ngx_calloc_buf(r->pool)) == NULL) {
665 return NGX_ERROR;
666 }
667
668 len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1;
669
670 b->memory = 1;
671 b->pos = ngx_palloc(r->pool, len);
672 if (b->pos == NULL) {
673 return NGX_ERROR;
674 }
675
676 b->last = ngx_sprintf(b->pos,
677 CRLF "--%0muA--" CRLF, ctx->boundary);
678
679 link->buf = b;
680 link->next = NULL;
681
682 return ngx_http_next_body_filter(r, link);
683 }
684
685 /* Initiate one or more subrequests for files to put in the ZIP archive */
686 static ngx_int_t
687 ngx_http_zip_send_pieces(ngx_http_request_t *r,
688 ngx_http_zip_ctx_t *ctx)
689 {
690 ngx_int_t rc = NGX_OK, pieces_sent = 0;
691 ngx_http_zip_piece_t *piece;
692 ngx_http_zip_range_t *range = NULL;
693
694 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
695 "mod_zip: sending pieces, starting with piece %d", ctx->pieces_i);
696
697 switch(ctx->ranges.nelts) {
698 case 0:
699 while (rc == NGX_OK && ctx->pieces_i < ctx->pieces_n) {
700 piece = &ctx->pieces[ctx->pieces_i++];
701 pieces_sent++;
702 rc = ngx_http_zip_send_piece(r, ctx, piece, NULL);
703 }
704 break;
705 case 1:
706 range = &((ngx_http_zip_range_t *)ctx->ranges.elts)[0];
707 while (rc == NGX_OK && ctx->pieces_i < ctx->pieces_n) {
708 piece = &ctx->pieces[ctx->pieces_i++];
709 if (ngx_http_zip_ranges_intersect(&piece->range, range)) {
710 pieces_sent++;
711 rc = ngx_http_zip_send_piece(r, ctx, piece, range);
712 }
713 }
714 break;
715 default:
716 while (rc == NGX_OK && ctx->ranges_i < ctx->ranges.nelts) {
717 range = &((ngx_http_zip_range_t *)ctx->ranges.elts)[ctx->ranges_i];
718 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
719 "mod_zip: sending range #%d start=%O end=%O (size %d)",
720 ctx->ranges_i, range->start, range->end, range->boundary_header.len);
721 rc = ngx_http_zip_send_boundary(r, ctx, range);
722 while (rc == NGX_OK && ctx->pieces_i < ctx->pieces_n) {
723 piece = &ctx->pieces[ctx->pieces_i++];
724 if (ngx_http_zip_ranges_intersect(&piece->range, range)) {
725 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
726 "mod_zip: sending range=%d piece=%d",
727 ctx->ranges_i, pieces_sent);
728 pieces_sent++;
729 rc = ngx_http_zip_send_piece(r, ctx, piece, range);
730 }
731 }
732
733 if (rc == NGX_OK) {
734 ctx->ranges_i++;
735 ctx->pieces_i = 0;
736 }
737 }
738
739 if (rc == NGX_OK) {
740 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
741 "mod_zip: sending final boundary");
742 rc = ngx_http_zip_send_final_boundary(r, ctx);
743 }
744 break;
745 }
746
747 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
748 "mod_zip: sent %d pieces, last rc = %d", pieces_sent, rc);
749
750 if (rc == NGX_OK) {
751 ctx->trailer_sent = 1;
752 return ngx_http_send_special(r, NGX_HTTP_LAST);
753 }
754
755 /* NGX_DONE, NGX_AGAIN or NGX_ERROR */
756 return rc;
757 }
758
759 /* Install the module filters */
760 static ngx_int_t
761 ngx_http_zip_init(ngx_conf_t *cf)
762 {
763 ngx_http_next_header_filter = ngx_http_top_header_filter;
764 ngx_http_top_header_filter = ngx_http_zip_header_filter;
765
766 ngx_http_next_body_filter = ngx_http_top_body_filter;
767 ngx_http_top_body_filter = ngx_http_zip_body_filter;
768
769 return NGX_OK;
770 }
Something went wrong with that request. Please try again.