1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11
12
13 /*
14 * the single part format:
15 *
16 * "HTTP/1.0 206 Partial Content" CRLF
17 * ... header ...
18 * "Content-Type: image/jpeg" CRLF
19 * "Content-Length: SIZE" CRLF
20 * "Content-Range: bytes START-END/SIZE" CRLF
21 * CRLF
22 * ... data ...
23 *
24 *
25 * the multipart format:
26 *
27 * "HTTP/1.0 206 Partial Content" CRLF
28 * ... header ...
29 * "Content-Type: multipart/byteranges; boundary=0123456789" CRLF
30 * CRLF
31 * CRLF
32 * "--0123456789" CRLF
33 * "Content-Type: image/jpeg" CRLF
34 * "Content-Range: bytes START0-END0/SIZE" CRLF
35 * CRLF
36 * ... data ...
37 * CRLF
38 * "--0123456789" CRLF
39 * "Content-Type: image/jpeg" CRLF
40 * "Content-Range: bytes START1-END1/SIZE" CRLF
41 * CRLF
42 * ... data ...
43 * CRLF
44 * "--0123456789--" CRLF
45 */
46
47
48 typedef struct {
49 off_t start;
50 off_t end;
51 ngx_str_t content_range;
52 } ngx_http_range_t;
53
54
55 typedef struct {
56 off_t offset;
57 ngx_str_t boundary_header;
58 ngx_array_t ranges;
59 } ngx_http_range_filter_ctx_t;
60
61
62 static ngx_int_t ngx_http_range_parse(ngx_http_request_t *r,
63 ngx_http_range_filter_ctx_t *ctx, ngx_uint_t ranges);
64 static ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r,
65 ngx_http_range_filter_ctx_t *ctx);
66 static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r,
67 ngx_http_range_filter_ctx_t *ctx);
68 static ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r);
69 static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r,
70 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
71 static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r,
72 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
73 static ngx_int_t ngx_http_range_multipart_body(ngx_http_request_t *r,
74 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
75
76 static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf);
77 static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf);
78
79
80 static ngx_http_module_t ngx_http_range_header_filter_module_ctx = {
81 NULL, /* preconfiguration */
82 ngx_http_range_header_filter_init, /* postconfiguration */
83
84 NULL, /* create main configuration */
85 NULL, /* init main configuration */
86
87 NULL, /* create server configuration */
88 NULL, /* merge server configuration */
89
90 NULL, /* create location configuration */
91 NULL, /* merge location configuration */
92 };
93
94
95 ngx_module_t ngx_http_range_header_filter_module = {
96 NGX_MODULE_V1,
97 &ngx_http_range_header_filter_module_ctx, /* module context */
98 NULL, /* module directives */
99 NGX_HTTP_MODULE, /* module type */
100 NULL, /* init master */
101 NULL, /* init module */
102 NULL, /* init process */
103 NULL, /* init thread */
104 NULL, /* exit thread */
105 NULL, /* exit process */
106 NULL, /* exit master */
107 NGX_MODULE_V1_PADDING
108 };
109
110
111 static ngx_http_module_t ngx_http_range_body_filter_module_ctx = {
112 NULL, /* preconfiguration */
113 ngx_http_range_body_filter_init, /* postconfiguration */
114
115 NULL, /* create main configuration */
116 NULL, /* init main configuration */
117
118 NULL, /* create server configuration */
119 NULL, /* merge server configuration */
120
121 NULL, /* create location configuration */
122 NULL, /* merge location configuration */
123 };
124
125
126 ngx_module_t ngx_http_range_body_filter_module = {
127 NGX_MODULE_V1,
128 &ngx_http_range_body_filter_module_ctx, /* module context */
129 NULL, /* module directives */
130 NGX_HTTP_MODULE, /* module type */
131 NULL, /* init master */
132 NULL, /* init module */
133 NULL, /* init process */
134 NULL, /* init thread */
135 NULL, /* exit thread */
136 NULL, /* exit process */
137 NULL, /* exit master */
138 NGX_MODULE_V1_PADDING
139 };
140
141
142 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
143 static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
144
145
146 static ngx_int_t
ngx_http_range_header_filter(ngx_http_request_t * r)147 ngx_http_range_header_filter(ngx_http_request_t *r)
148 {
149 time_t if_range_time;
150 ngx_str_t *if_range, *etag;
151 ngx_uint_t ranges;
152 ngx_http_core_loc_conf_t *clcf;
153 ngx_http_range_filter_ctx_t *ctx;
154
155 if (r->http_version < NGX_HTTP_VERSION_10
156 || r->headers_out.status != NGX_HTTP_OK
157 || (r != r->main && !r->subrequest_ranges)
158 || r->headers_out.content_length_n == -1
159 || !r->allow_ranges)
160 {
161 return ngx_http_next_header_filter(r);
162 }
163
164 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
165
166 if (clcf->max_ranges == 0) {
167 return ngx_http_next_header_filter(r);
168 }
169
170 if (r->headers_in.range == NULL
171 || r->headers_in.range->value.len < 7
172 || ngx_strncasecmp(r->headers_in.range->value.data,
173 (u_char *) "bytes=", 6)
174 != 0)
175 {
176 goto next_filter;
177 }
178
179 if (r->headers_in.if_range) {
180
181 if_range = &r->headers_in.if_range->value;
182
183 if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '"') {
184
185 if (r->headers_out.etag == NULL) {
186 goto next_filter;
187 }
188
189 etag = &r->headers_out.etag->value;
190
191 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
192 "http ir:%V etag:%V", if_range, etag);
193
194 if (if_range->len != etag->len
195 || ngx_strncmp(if_range->data, etag->data, etag->len) != 0)
196 {
197 goto next_filter;
198 }
199
200 goto parse;
201 }
202
203 if (r->headers_out.last_modified_time == (time_t) -1) {
204 goto next_filter;
205 }
206
207 if_range_time = ngx_parse_http_time(if_range->data, if_range->len);
208
209 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
210 "http ir:%T lm:%T",
211 if_range_time, r->headers_out.last_modified_time);
212
213 if (if_range_time != r->headers_out.last_modified_time) {
214 goto next_filter;
215 }
216 }
217
218 parse:
219
220 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
221 if (ctx == NULL) {
222 return NGX_ERROR;
223 }
224
225 ctx->offset = r->headers_out.content_offset;
226
227 ranges = r->single_range ? 1 : clcf->max_ranges;
228
229 switch (ngx_http_range_parse(r, ctx, ranges)) {
230
231 case NGX_OK:
232 ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
233
234 r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
235 r->headers_out.status_line.len = 0;
236
237 if (ctx->ranges.nelts == 1) {
238 return ngx_http_range_singlepart_header(r, ctx);
239 }
240
241 return ngx_http_range_multipart_header(r, ctx);
242
243 case NGX_HTTP_RANGE_NOT_SATISFIABLE:
244 return ngx_http_range_not_satisfiable(r);
245
246 case NGX_ERROR:
247 return NGX_ERROR;
248
249 default: /* NGX_DECLINED */
250 break;
251 }
252
253 next_filter:
254
255 r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
256 if (r->headers_out.accept_ranges == NULL) {
257 return NGX_ERROR;
258 }
259
260 r->headers_out.accept_ranges->hash = 1;
261 ngx_str_set(&r->headers_out.accept_ranges->key, "Accept-Ranges");
262 ngx_str_set(&r->headers_out.accept_ranges->value, "bytes");
263
264 return ngx_http_next_header_filter(r);
265 }
266
267
268 static ngx_int_t
ngx_http_range_parse(ngx_http_request_t * r,ngx_http_range_filter_ctx_t * ctx,ngx_uint_t ranges)269 ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx,
270 ngx_uint_t ranges)
271 {
272 u_char *p;
273 off_t start, end, size, content_length, cutoff,
274 cutlim;
275 ngx_uint_t suffix;
276 ngx_http_range_t *range;
277 ngx_http_range_filter_ctx_t *mctx;
278
279 if (r != r->main) {
280 mctx = ngx_http_get_module_ctx(r->main,
281 ngx_http_range_body_filter_module);
282 if (mctx) {
283 ctx->ranges = mctx->ranges;
284 return NGX_OK;
285 }
286 }
287
288 if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))
289 != NGX_OK)
290 {
291 return NGX_ERROR;
292 }
293
294 p = r->headers_in.range->value.data + 6;
295 size = 0;
296 content_length = r->headers_out.content_length_n;
297
298 cutoff = NGX_MAX_OFF_T_VALUE / 10;
299 cutlim = NGX_MAX_OFF_T_VALUE % 10;
300
301 for ( ;; ) {
302 start = 0;
303 end = 0;
304 suffix = 0;
305
306 while (*p == ' ') { p++; }
307
308 if (*p != '-') {
309 if (*p < '0' || *p > '9') {
310 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
311 }
312
313 while (*p >= '0' && *p <= '9') {
314 if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {
315 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
316 }
317
318 start = start * 10 + (*p++ - '0');
319 }
320
321 while (*p == ' ') { p++; }
322
323 if (*p++ != '-') {
324 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
325 }
326
327 while (*p == ' ') { p++; }
328
329 if (*p == ',' || *p == '\0') {
330 end = content_length;
331 goto found;
332 }
333
334 } else {
335 suffix = 1;
336 p++;
337 }
338
339 if (*p < '0' || *p > '9') {
340 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
341 }
342
343 while (*p >= '0' && *p <= '9') {
344 if (end >= cutoff && (end > cutoff || *p - '0' > cutlim)) {
345 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
346 }
347
348 end = end * 10 + (*p++ - '0');
349 }
350
351 while (*p == ' ') { p++; }
352
353 if (*p != ',' && *p != '\0') {
354 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
355 }
356
357 if (suffix) {
358 start = (end < content_length) ? content_length - end : 0;
359 end = content_length - 1;
360 }
361
362 if (end >= content_length) {
363 end = content_length;
364
365 } else {
366 end++;
367 }
368
369 found:
370
371 if (start < end) {
372 range = ngx_array_push(&ctx->ranges);
373 if (range == NULL) {
374 return NGX_ERROR;
375 }
376
377 range->start = start;
378 range->end = end;
379
380 if (size > NGX_MAX_OFF_T_VALUE - (end - start)) {
381 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
382 }
383
384 size += end - start;
385
386 if (ranges-- == 0) {
387 return NGX_DECLINED;
388 }
389
390 } else if (start == 0) {
391 return NGX_DECLINED;
392 }
393
394 if (*p++ != ',') {
395 break;
396 }
397 }
398
399 if (ctx->ranges.nelts == 0) {
400 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
401 }
402
403 if (size > content_length) {
404 return NGX_DECLINED;
405 }
406
407 return NGX_OK;
408 }
409
410
411 static ngx_int_t
ngx_http_range_singlepart_header(ngx_http_request_t * r,ngx_http_range_filter_ctx_t * ctx)412 ngx_http_range_singlepart_header(ngx_http_request_t *r,
413 ngx_http_range_filter_ctx_t *ctx)
414 {
415 ngx_table_elt_t *content_range;
416 ngx_http_range_t *range;
417
418 if (r != r->main) {
419 return ngx_http_next_header_filter(r);
420 }
421
422 content_range = ngx_list_push(&r->headers_out.headers);
423 if (content_range == NULL) {
424 return NGX_ERROR;
425 }
426
427 r->headers_out.content_range = content_range;
428
429 content_range->hash = 1;
430 ngx_str_set(&content_range->key, "Content-Range");
431
432 content_range->value.data = ngx_pnalloc(r->pool,
433 sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
434 if (content_range->value.data == NULL) {
435 content_range->hash = 0;
436 r->headers_out.content_range = NULL;
437 return NGX_ERROR;
438 }
439
440 /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
441
442 range = ctx->ranges.elts;
443
444 content_range->value.len = ngx_sprintf(content_range->value.data,
445 "bytes %O-%O/%O",
446 range->start, range->end - 1,
447 r->headers_out.content_length_n)
448 - content_range->value.data;
449
450 r->headers_out.content_length_n = range->end - range->start;
451 r->headers_out.content_offset = range->start;
452
453 if (r->headers_out.content_length) {
454 r->headers_out.content_length->hash = 0;
455 r->headers_out.content_length = NULL;
456 }
457
458 return ngx_http_next_header_filter(r);
459 }
460
461
462 static ngx_int_t
ngx_http_range_multipart_header(ngx_http_request_t * r,ngx_http_range_filter_ctx_t * ctx)463 ngx_http_range_multipart_header(ngx_http_request_t *r,
464 ngx_http_range_filter_ctx_t *ctx)
465 {
466 off_t len;
467 size_t size;
468 ngx_uint_t i;
469 ngx_http_range_t *range;
470 ngx_atomic_uint_t boundary;
471
472 size = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
473 + sizeof(CRLF "Content-Type: ") - 1
474 + r->headers_out.content_type.len
475 + sizeof(CRLF "Content-Range: bytes ") - 1;
476
477 if (r->headers_out.content_type_len == r->headers_out.content_type.len
478 && r->headers_out.charset.len)
479 {
480 size += sizeof("; charset=") - 1 + r->headers_out.charset.len;
481 }
482
483 ctx->boundary_header.data = ngx_pnalloc(r->pool, size);
484 if (ctx->boundary_header.data == NULL) {
485 return NGX_ERROR;
486 }
487
488 boundary = ngx_next_temp_number(0);
489
490 /*
491 * The boundary header of the range:
492 * CRLF
493 * "--0123456789" CRLF
494 * "Content-Type: image/jpeg" CRLF
495 * "Content-Range: bytes "
496 */
497
498 if (r->headers_out.content_type_len == r->headers_out.content_type.len
499 && r->headers_out.charset.len)
500 {
501 ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
502 CRLF "--%0muA" CRLF
503 "Content-Type: %V; charset=%V" CRLF
504 "Content-Range: bytes ",
505 boundary,
506 &r->headers_out.content_type,
507 &r->headers_out.charset)
508 - ctx->boundary_header.data;
509
510 } else if (r->headers_out.content_type.len) {
511 ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
512 CRLF "--%0muA" CRLF
513 "Content-Type: %V" CRLF
514 "Content-Range: bytes ",
515 boundary,
516 &r->headers_out.content_type)
517 - ctx->boundary_header.data;
518
519 } else {
520 ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
521 CRLF "--%0muA" CRLF
522 "Content-Range: bytes ",
523 boundary)
524 - ctx->boundary_header.data;
525 }
526
527 r->headers_out.content_type.data =
528 ngx_pnalloc(r->pool,
529 sizeof("Content-Type: multipart/byteranges; boundary=") - 1
530 + NGX_ATOMIC_T_LEN);
531
532 if (r->headers_out.content_type.data == NULL) {
533 return NGX_ERROR;
534 }
535
536 r->headers_out.content_type_lowcase = NULL;
537
538 /* "Content-Type: multipart/byteranges; boundary=0123456789" */
539
540 r->headers_out.content_type.len =
541 ngx_sprintf(r->headers_out.content_type.data,
542 "multipart/byteranges; boundary=%0muA",
543 boundary)
544 - r->headers_out.content_type.data;
545
546 r->headers_out.content_type_len = r->headers_out.content_type.len;
547
548 r->headers_out.charset.len = 0;
549
550 /* the size of the last boundary CRLF "--0123456789--" CRLF */
551
552 len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1;
553
554 range = ctx->ranges.elts;
555 for (i = 0; i < ctx->ranges.nelts; i++) {
556
557 /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */
558
559 range[i].content_range.data =
560 ngx_pnalloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4);
561
562 if (range[i].content_range.data == NULL) {
563 return NGX_ERROR;
564 }
565
566 range[i].content_range.len = ngx_sprintf(range[i].content_range.data,
567 "%O-%O/%O" CRLF CRLF,
568 range[i].start, range[i].end - 1,
569 r->headers_out.content_length_n)
570 - range[i].content_range.data;
571
572 len += ctx->boundary_header.len + range[i].content_range.len
573 + (range[i].end - range[i].start);
574 }
575
576 r->headers_out.content_length_n = len;
577
578 if (r->headers_out.content_length) {
579 r->headers_out.content_length->hash = 0;
580 r->headers_out.content_length = NULL;
581 }
582
583 return ngx_http_next_header_filter(r);
584 }
585
586
587 static ngx_int_t
ngx_http_range_not_satisfiable(ngx_http_request_t * r)588 ngx_http_range_not_satisfiable(ngx_http_request_t *r)
589 {
590 ngx_table_elt_t *content_range;
591
592 r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
593
594 content_range = ngx_list_push(&r->headers_out.headers);
595 if (content_range == NULL) {
596 return NGX_ERROR;
597 }
598
599 r->headers_out.content_range = content_range;
600
601 content_range->hash = 1;
602 ngx_str_set(&content_range->key, "Content-Range");
603
604 content_range->value.data = ngx_pnalloc(r->pool,
605 sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
606 if (content_range->value.data == NULL) {
607 content_range->hash = 0;
608 r->headers_out.content_range = NULL;
609 return NGX_ERROR;
610 }
611
612 content_range->value.len = ngx_sprintf(content_range->value.data,
613 "bytes */%O",
614 r->headers_out.content_length_n)
615 - content_range->value.data;
616
617 ngx_http_clear_content_length(r);
618
619 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
620 }
621
622
623 static ngx_int_t
ngx_http_range_body_filter(ngx_http_request_t * r,ngx_chain_t * in)624 ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
625 {
626 ngx_http_range_filter_ctx_t *ctx;
627
628 if (in == NULL) {
629 return ngx_http_next_body_filter(r, in);
630 }
631
632 ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);
633
634 if (ctx == NULL) {
635 return ngx_http_next_body_filter(r, in);
636 }
637
638 if (ctx->ranges.nelts == 1) {
639 return ngx_http_range_singlepart_body(r, ctx, in);
640 }
641
642 /*
643 * multipart ranges are supported only if whole body is in a single buffer
644 */
645
646 if (ngx_buf_special(in->buf)) {
647 return ngx_http_next_body_filter(r, in);
648 }
649
650 if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {
651 return NGX_ERROR;
652 }
653
654 return ngx_http_range_multipart_body(r, ctx, in);
655 }
656
657
658 static ngx_int_t
ngx_http_range_test_overlapped(ngx_http_request_t * r,ngx_http_range_filter_ctx_t * ctx,ngx_chain_t * in)659 ngx_http_range_test_overlapped(ngx_http_request_t *r,
660 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
661 {
662 off_t start, last;
663 ngx_buf_t *buf;
664 ngx_uint_t i;
665 ngx_http_range_t *range;
666
667 if (ctx->offset) {
668 goto overlapped;
669 }
670
671 buf = in->buf;
672
673 if (!buf->last_buf) {
674 start = ctx->offset;
675 last = ctx->offset + ngx_buf_size(buf);
676
677 range = ctx->ranges.elts;
678 for (i = 0; i < ctx->ranges.nelts; i++) {
679 if (start > range[i].start || last < range[i].end) {
680 goto overlapped;
681 }
682 }
683 }
684
685 ctx->offset = ngx_buf_size(buf);
686
687 return NGX_OK;
688
689 overlapped:
690
691 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
692 "range in overlapped buffers");
693
694 return NGX_ERROR;
695 }
696
697
698 static ngx_int_t
ngx_http_range_singlepart_body(ngx_http_request_t * r,ngx_http_range_filter_ctx_t * ctx,ngx_chain_t * in)699 ngx_http_range_singlepart_body(ngx_http_request_t *r,
700 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
701 {
702 off_t start, last;
703 ngx_int_t rc;
704 ngx_buf_t *buf;
705 ngx_chain_t *out, *cl, *tl, **ll;
706 ngx_http_range_t *range;
707
708 out = NULL;
709 ll = &out;
710 range = ctx->ranges.elts;
711
712 for (cl = in; cl; cl = cl->next) {
713
714 buf = cl->buf;
715
716 start = ctx->offset;
717 last = ctx->offset + ngx_buf_size(buf);
718
719 ctx->offset = last;
720
721 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
722 "http range body buf: %O-%O", start, last);
723
724 if (ngx_buf_special(buf)) {
725
726 if (range->end <= start) {
727 continue;
728 }
729
730 tl = ngx_alloc_chain_link(r->pool);
731 if (tl == NULL) {
732 return NGX_ERROR;
733 }
734
735 tl->buf = buf;
736 tl->next = NULL;
737
738 *ll = tl;
739 ll = &tl->next;
740
741 continue;
742 }
743
744 if (range->end <= start || range->start >= last) {
745
746 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
747 "http range body skip");
748
749 if (buf->in_file) {
750 buf->file_pos = buf->file_last;
751 }
752
753 buf->pos = buf->last;
754 buf->sync = 1;
755
756 continue;
757 }
758
759 if (range->start > start) {
760
761 if (buf->in_file) {
762 buf->file_pos += range->start - start;
763 }
764
765 if (ngx_buf_in_memory(buf)) {
766 buf->pos += (size_t) (range->start - start);
767 }
768 }
769
770 if (range->end <= last) {
771
772 if (buf->in_file) {
773 buf->file_last -= last - range->end;
774 }
775
776 if (ngx_buf_in_memory(buf)) {
777 buf->last -= (size_t) (last - range->end);
778 }
779
780 buf->last_buf = (r == r->main) ? 1 : 0;
781 buf->last_in_chain = 1;
782
783 tl = ngx_alloc_chain_link(r->pool);
784 if (tl == NULL) {
785 return NGX_ERROR;
786 }
787
788 tl->buf = buf;
789 tl->next = NULL;
790
791 *ll = tl;
792 ll = &tl->next;
793
794 continue;
795 }
796
797 tl = ngx_alloc_chain_link(r->pool);
798 if (tl == NULL) {
799 return NGX_ERROR;
800 }
801
802 tl->buf = buf;
803 tl->next = NULL;
804
805 *ll = tl;
806 ll = &tl->next;
807 }
808
809 rc = ngx_http_next_body_filter(r, out);
810
811 while (out) {
812 cl = out;
813 out = out->next;
814 ngx_free_chain(r->pool, cl);
815 }
816
817 return rc;
818 }
819
820
821 static ngx_int_t
ngx_http_range_multipart_body(ngx_http_request_t * r,ngx_http_range_filter_ctx_t * ctx,ngx_chain_t * in)822 ngx_http_range_multipart_body(ngx_http_request_t *r,
823 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
824 {
825 ngx_buf_t *b, *buf;
826 ngx_uint_t i;
827 ngx_chain_t *out, *hcl, *rcl, *dcl, **ll;
828 ngx_http_range_t *range;
829
830 ll = &out;
831 buf = in->buf;
832 range = ctx->ranges.elts;
833
834 for (i = 0; i < ctx->ranges.nelts; i++) {
835
836 /*
837 * The boundary header of the range:
838 * CRLF
839 * "--0123456789" CRLF
840 * "Content-Type: image/jpeg" CRLF
841 * "Content-Range: bytes "
842 */
843
844 b = ngx_calloc_buf(r->pool);
845 if (b == NULL) {
846 return NGX_ERROR;
847 }
848
849 b->memory = 1;
850 b->pos = ctx->boundary_header.data;
851 b->last = ctx->boundary_header.data + ctx->boundary_header.len;
852
853 hcl = ngx_alloc_chain_link(r->pool);
854 if (hcl == NULL) {
855 return NGX_ERROR;
856 }
857
858 hcl->buf = b;
859
860
861 /* "SSSS-EEEE/TTTT" CRLF CRLF */
862
863 b = ngx_calloc_buf(r->pool);
864 if (b == NULL) {
865 return NGX_ERROR;
866 }
867
868 b->temporary = 1;
869 b->pos = range[i].content_range.data;
870 b->last = range[i].content_range.data + range[i].content_range.len;
871
872 rcl = ngx_alloc_chain_link(r->pool);
873 if (rcl == NULL) {
874 return NGX_ERROR;
875 }
876
877 rcl->buf = b;
878
879
880 /* the range data */
881
882 b = ngx_calloc_buf(r->pool);
883 if (b == NULL) {
884 return NGX_ERROR;
885 }
886
887 b->in_file = buf->in_file;
888 b->temporary = buf->temporary;
889 b->memory = buf->memory;
890 b->mmap = buf->mmap;
891 b->file = buf->file;
892
893 if (buf->in_file) {
894 b->file_pos = buf->file_pos + range[i].start;
895 b->file_last = buf->file_pos + range[i].end;
896 }
897
898 if (ngx_buf_in_memory(buf)) {
899 b->pos = buf->pos + (size_t) range[i].start;
900 b->last = buf->pos + (size_t) range[i].end;
901 }
902
903 dcl = ngx_alloc_chain_link(r->pool);
904 if (dcl == NULL) {
905 return NGX_ERROR;
906 }
907
908 dcl->buf = b;
909
910 *ll = hcl;
911 hcl->next = rcl;
912 rcl->next = dcl;
913 ll = &dcl->next;
914 }
915
916 /* the last boundary CRLF "--0123456789--" CRLF */
917
918 b = ngx_calloc_buf(r->pool);
919 if (b == NULL) {
920 return NGX_ERROR;
921 }
922
923 b->temporary = 1;
924 b->last_buf = 1;
925
926 b->pos = ngx_pnalloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
927 + sizeof("--" CRLF) - 1);
928 if (b->pos == NULL) {
929 return NGX_ERROR;
930 }
931
932 b->last = ngx_cpymem(b->pos, ctx->boundary_header.data,
933 sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN);
934 *b->last++ = '-'; *b->last++ = '-';
935 *b->last++ = CR; *b->last++ = LF;
936
937 hcl = ngx_alloc_chain_link(r->pool);
938 if (hcl == NULL) {
939 return NGX_ERROR;
940 }
941
942 hcl->buf = b;
943 hcl->next = NULL;
944
945 *ll = hcl;
946
947 return ngx_http_next_body_filter(r, out);
948 }
949
950
951 static ngx_int_t
ngx_http_range_header_filter_init(ngx_conf_t * cf)952 ngx_http_range_header_filter_init(ngx_conf_t *cf)
953 {
954 ngx_http_next_header_filter = ngx_http_top_header_filter;
955 ngx_http_top_header_filter = ngx_http_range_header_filter;
956
957 return NGX_OK;
958 }
959
960
961 static ngx_int_t
ngx_http_range_body_filter_init(ngx_conf_t * cf)962 ngx_http_range_body_filter_init(ngx_conf_t *cf)
963 {
964 ngx_http_next_body_filter = ngx_http_top_body_filter;
965 ngx_http_top_body_filter = ngx_http_range_body_filter;
966
967 return NGX_OK;
968 }
969