Annotation of embedaddon/nginx/src/http/modules/ngx_http_range_filter_module.c, revision 1.1.1.1
1.1 misho 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 mutlipart 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
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_http_core_loc_conf_t *clcf;
152: ngx_http_range_filter_ctx_t *ctx;
153:
154: if (r->http_version < NGX_HTTP_VERSION_10
155: || r->headers_out.status != NGX_HTTP_OK
156: || r != r->main
157: || r->headers_out.content_length_n == -1
158: || !r->allow_ranges)
159: {
160: return ngx_http_next_header_filter(r);
161: }
162:
163: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
164:
165: if (clcf->max_ranges == 0) {
166: return ngx_http_next_header_filter(r);
167: }
168:
169: if (r->headers_in.range == NULL
170: || r->headers_in.range->value.len < 7
171: || ngx_strncasecmp(r->headers_in.range->value.data,
172: (u_char *) "bytes=", 6)
173: != 0)
174: {
175: goto next_filter;
176: }
177:
178: if (r->headers_in.if_range) {
179:
180: if_range = &r->headers_in.if_range->value;
181:
182: if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '"') {
183:
184: if (r->headers_out.etag == NULL) {
185: goto next_filter;
186: }
187:
188: etag = &r->headers_out.etag->value;
189:
190: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
191: "http ir:%V etag:%V", if_range, etag);
192:
193: if (if_range->len != etag->len
194: || ngx_strncmp(if_range->data, etag->data, etag->len) != 0)
195: {
196: goto next_filter;
197: }
198:
199: goto parse;
200: }
201:
202: if (r->headers_out.last_modified_time == (time_t) -1) {
203: goto next_filter;
204: }
205:
206: if_range_time = ngx_http_parse_time(if_range->data, if_range->len);
207:
208: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
209: "http ir:%d lm:%d",
210: if_range_time, r->headers_out.last_modified_time);
211:
212: if (if_range_time != r->headers_out.last_modified_time) {
213: goto next_filter;
214: }
215: }
216:
217: parse:
218:
219: ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
220: if (ctx == NULL) {
221: return NGX_ERROR;
222: }
223:
224: if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))
225: != NGX_OK)
226: {
227: return NGX_ERROR;
228: }
229:
230: switch (ngx_http_range_parse(r, ctx, clcf->max_ranges)) {
231:
232: case NGX_OK:
233: ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
234:
235: r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
236: r->headers_out.status_line.len = 0;
237:
238: if (ctx->ranges.nelts == 1) {
239: return ngx_http_range_singlepart_header(r, ctx);
240: }
241:
242: return ngx_http_range_multipart_header(r, ctx);
243:
244: case NGX_HTTP_RANGE_NOT_SATISFIABLE:
245: return ngx_http_range_not_satisfiable(r);
246:
247: case NGX_ERROR:
248: return NGX_ERROR;
249:
250: default: /* NGX_DECLINED */
251: break;
252: }
253:
254: next_filter:
255:
256: r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
257: if (r->headers_out.accept_ranges == NULL) {
258: return NGX_ERROR;
259: }
260:
261: r->headers_out.accept_ranges->hash = 1;
262: ngx_str_set(&r->headers_out.accept_ranges->key, "Accept-Ranges");
263: ngx_str_set(&r->headers_out.accept_ranges->value, "bytes");
264:
265: return ngx_http_next_header_filter(r);
266: }
267:
268:
269: static ngx_int_t
270: ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx,
271: ngx_uint_t ranges)
272: {
273: u_char *p;
274: off_t start, end, size, content_length;
275: ngx_uint_t suffix;
276: ngx_http_range_t *range;
277:
278: p = r->headers_in.range->value.data + 6;
279: size = 0;
280: content_length = r->headers_out.content_length_n;
281:
282: for ( ;; ) {
283: start = 0;
284: end = 0;
285: suffix = 0;
286:
287: while (*p == ' ') { p++; }
288:
289: if (*p != '-') {
290: if (*p < '0' || *p > '9') {
291: return NGX_HTTP_RANGE_NOT_SATISFIABLE;
292: }
293:
294: while (*p >= '0' && *p <= '9') {
295: start = start * 10 + *p++ - '0';
296: }
297:
298: while (*p == ' ') { p++; }
299:
300: if (*p++ != '-') {
301: return NGX_HTTP_RANGE_NOT_SATISFIABLE;
302: }
303:
304: while (*p == ' ') { p++; }
305:
306: if (*p == ',' || *p == '\0') {
307: end = content_length;
308: goto found;
309: }
310:
311: } else {
312: suffix = 1;
313: p++;
314: }
315:
316: if (*p < '0' || *p > '9') {
317: return NGX_HTTP_RANGE_NOT_SATISFIABLE;
318: }
319:
320: while (*p >= '0' && *p <= '9') {
321: end = end * 10 + *p++ - '0';
322: }
323:
324: while (*p == ' ') { p++; }
325:
326: if (*p != ',' && *p != '\0') {
327: return NGX_HTTP_RANGE_NOT_SATISFIABLE;
328: }
329:
330: if (suffix) {
331: start = content_length - end;
332: end = content_length - 1;
333: }
334:
335: if (end >= content_length) {
336: end = content_length;
337:
338: } else {
339: end++;
340: }
341:
342: found:
343:
344: if (start < end) {
345: range = ngx_array_push(&ctx->ranges);
346: if (range == NULL) {
347: return NGX_ERROR;
348: }
349:
350: range->start = start;
351: range->end = end;
352:
353: size += end - start;
354:
355: if (ranges-- == 0) {
356: return NGX_DECLINED;
357: }
358: }
359:
360: if (*p++ != ',') {
361: break;
362: }
363: }
364:
365: if (ctx->ranges.nelts == 0) {
366: return NGX_HTTP_RANGE_NOT_SATISFIABLE;
367: }
368:
369: if (size > content_length) {
370: return NGX_DECLINED;
371: }
372:
373: return NGX_OK;
374: }
375:
376:
377: static ngx_int_t
378: ngx_http_range_singlepart_header(ngx_http_request_t *r,
379: ngx_http_range_filter_ctx_t *ctx)
380: {
381: ngx_table_elt_t *content_range;
382: ngx_http_range_t *range;
383:
384: content_range = ngx_list_push(&r->headers_out.headers);
385: if (content_range == NULL) {
386: return NGX_ERROR;
387: }
388:
389: r->headers_out.content_range = content_range;
390:
391: content_range->hash = 1;
392: ngx_str_set(&content_range->key, "Content-Range");
393:
394: content_range->value.data = ngx_pnalloc(r->pool,
395: sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
396: if (content_range->value.data == NULL) {
397: return NGX_ERROR;
398: }
399:
400: /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
401:
402: range = ctx->ranges.elts;
403:
404: content_range->value.len = ngx_sprintf(content_range->value.data,
405: "bytes %O-%O/%O",
406: range->start, range->end - 1,
407: r->headers_out.content_length_n)
408: - content_range->value.data;
409:
410: r->headers_out.content_length_n = range->end - range->start;
411:
412: if (r->headers_out.content_length) {
413: r->headers_out.content_length->hash = 0;
414: r->headers_out.content_length = NULL;
415: }
416:
417: return ngx_http_next_header_filter(r);
418: }
419:
420:
421: static ngx_int_t
422: ngx_http_range_multipart_header(ngx_http_request_t *r,
423: ngx_http_range_filter_ctx_t *ctx)
424: {
425: size_t len;
426: ngx_uint_t i;
427: ngx_http_range_t *range;
428: ngx_atomic_uint_t boundary;
429:
430: len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
431: + sizeof(CRLF "Content-Type: ") - 1
432: + r->headers_out.content_type.len
433: + sizeof(CRLF "Content-Range: bytes ") - 1;
434:
435: if (r->headers_out.charset.len) {
436: len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
437: }
438:
439: ctx->boundary_header.data = ngx_pnalloc(r->pool, len);
440: if (ctx->boundary_header.data == NULL) {
441: return NGX_ERROR;
442: }
443:
444: boundary = ngx_next_temp_number(0);
445:
446: /*
447: * The boundary header of the range:
448: * CRLF
449: * "--0123456789" CRLF
450: * "Content-Type: image/jpeg" CRLF
451: * "Content-Range: bytes "
452: */
453:
454: if (r->headers_out.charset.len) {
455: ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
456: CRLF "--%0muA" CRLF
457: "Content-Type: %V; charset=%V" CRLF
458: "Content-Range: bytes ",
459: boundary,
460: &r->headers_out.content_type,
461: &r->headers_out.charset)
462: - ctx->boundary_header.data;
463:
464: r->headers_out.charset.len = 0;
465:
466: } else if (r->headers_out.content_type.len) {
467: ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
468: CRLF "--%0muA" CRLF
469: "Content-Type: %V" CRLF
470: "Content-Range: bytes ",
471: boundary,
472: &r->headers_out.content_type)
473: - ctx->boundary_header.data;
474:
475: } else {
476: ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
477: CRLF "--%0muA" CRLF
478: "Content-Range: bytes ",
479: boundary)
480: - ctx->boundary_header.data;
481: }
482:
483: r->headers_out.content_type.data =
484: ngx_pnalloc(r->pool,
485: sizeof("Content-Type: multipart/byteranges; boundary=") - 1
486: + NGX_ATOMIC_T_LEN);
487:
488: if (r->headers_out.content_type.data == NULL) {
489: return NGX_ERROR;
490: }
491:
492: r->headers_out.content_type_lowcase = NULL;
493:
494: /* "Content-Type: multipart/byteranges; boundary=0123456789" */
495:
496: r->headers_out.content_type.len =
497: ngx_sprintf(r->headers_out.content_type.data,
498: "multipart/byteranges; boundary=%0muA",
499: boundary)
500: - r->headers_out.content_type.data;
501:
502: r->headers_out.content_type_len = r->headers_out.content_type.len;
503:
504: /* the size of the last boundary CRLF "--0123456789--" CRLF */
505:
506: len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1;
507:
508: range = ctx->ranges.elts;
509: for (i = 0; i < ctx->ranges.nelts; i++) {
510:
511: /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */
512:
513: range[i].content_range.data =
514: ngx_pnalloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4);
515:
516: if (range[i].content_range.data == NULL) {
517: return NGX_ERROR;
518: }
519:
520: range[i].content_range.len = ngx_sprintf(range[i].content_range.data,
521: "%O-%O/%O" CRLF CRLF,
522: range[i].start, range[i].end - 1,
523: r->headers_out.content_length_n)
524: - range[i].content_range.data;
525:
526: len += ctx->boundary_header.len + range[i].content_range.len
527: + (size_t) (range[i].end - range[i].start);
528: }
529:
530: r->headers_out.content_length_n = len;
531:
532: if (r->headers_out.content_length) {
533: r->headers_out.content_length->hash = 0;
534: r->headers_out.content_length = NULL;
535: }
536:
537: return ngx_http_next_header_filter(r);
538: }
539:
540:
541: static ngx_int_t
542: ngx_http_range_not_satisfiable(ngx_http_request_t *r)
543: {
544: ngx_table_elt_t *content_range;
545:
546: r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
547:
548: content_range = ngx_list_push(&r->headers_out.headers);
549: if (content_range == NULL) {
550: return NGX_ERROR;
551: }
552:
553: r->headers_out.content_range = content_range;
554:
555: content_range->hash = 1;
556: ngx_str_set(&content_range->key, "Content-Range");
557:
558: content_range->value.data = ngx_pnalloc(r->pool,
559: sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
560: if (content_range->value.data == NULL) {
561: return NGX_ERROR;
562: }
563:
564: content_range->value.len = ngx_sprintf(content_range->value.data,
565: "bytes */%O",
566: r->headers_out.content_length_n)
567: - content_range->value.data;
568:
569: ngx_http_clear_content_length(r);
570:
571: return NGX_HTTP_RANGE_NOT_SATISFIABLE;
572: }
573:
574:
575: static ngx_int_t
576: ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
577: {
578: ngx_http_range_filter_ctx_t *ctx;
579:
580: if (in == NULL) {
581: return ngx_http_next_body_filter(r, in);
582: }
583:
584: ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);
585:
586: if (ctx == NULL) {
587: return ngx_http_next_body_filter(r, in);
588: }
589:
590: if (ctx->ranges.nelts == 1) {
591: return ngx_http_range_singlepart_body(r, ctx, in);
592: }
593:
594: /*
595: * multipart ranges are supported only if whole body is in a single buffer
596: */
597:
598: if (ngx_buf_special(in->buf)) {
599: return ngx_http_next_body_filter(r, in);
600: }
601:
602: if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {
603: return NGX_ERROR;
604: }
605:
606: return ngx_http_range_multipart_body(r, ctx, in);
607: }
608:
609:
610: static ngx_int_t
611: ngx_http_range_test_overlapped(ngx_http_request_t *r,
612: ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
613: {
614: off_t start, last;
615: ngx_buf_t *buf;
616: ngx_uint_t i;
617: ngx_http_range_t *range;
618:
619: if (ctx->offset) {
620: goto overlapped;
621: }
622:
623: buf = in->buf;
624:
625: if (!buf->last_buf) {
626: start = ctx->offset;
627: last = ctx->offset + ngx_buf_size(buf);
628:
629: range = ctx->ranges.elts;
630: for (i = 0; i < ctx->ranges.nelts; i++) {
631: if (start > range[i].start || last < range[i].end) {
632: goto overlapped;
633: }
634: }
635: }
636:
637: ctx->offset = ngx_buf_size(buf);
638:
639: return NGX_OK;
640:
641: overlapped:
642:
643: ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
644: "range in overlapped buffers");
645:
646: return NGX_ERROR;
647: }
648:
649:
650: static ngx_int_t
651: ngx_http_range_singlepart_body(ngx_http_request_t *r,
652: ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
653: {
654: off_t start, last;
655: ngx_buf_t *buf;
656: ngx_chain_t *out, *cl, **ll;
657: ngx_http_range_t *range;
658:
659: out = NULL;
660: ll = &out;
661: range = ctx->ranges.elts;
662:
663: for (cl = in; cl; cl = cl->next) {
664:
665: buf = cl->buf;
666:
667: start = ctx->offset;
668: last = ctx->offset + ngx_buf_size(buf);
669:
670: ctx->offset = last;
671:
672: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
673: "http range body buf: %O-%O", start, last);
674:
675: if (ngx_buf_special(buf)) {
676: *ll = cl;
677: ll = &cl->next;
678: continue;
679: }
680:
681: if (range->end <= start || range->start >= last) {
682:
683: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
684: "http range body skip");
685:
686: if (buf->in_file) {
687: buf->file_pos = buf->file_last;
688: }
689:
690: buf->pos = buf->last;
691: buf->sync = 1;
692:
693: continue;
694: }
695:
696: if (range->start > start) {
697:
698: if (buf->in_file) {
699: buf->file_pos += range->start - start;
700: }
701:
702: if (ngx_buf_in_memory(buf)) {
703: buf->pos += (size_t) (range->start - start);
704: }
705: }
706:
707: if (range->end <= last) {
708:
709: if (buf->in_file) {
710: buf->file_last -= last - range->end;
711: }
712:
713: if (ngx_buf_in_memory(buf)) {
714: buf->last -= (size_t) (last - range->end);
715: }
716:
717: buf->last_buf = 1;
718: *ll = cl;
719: cl->next = NULL;
720:
721: break;
722: }
723:
724: *ll = cl;
725: ll = &cl->next;
726: }
727:
728: if (out == NULL) {
729: return NGX_OK;
730: }
731:
732: return ngx_http_next_body_filter(r, out);
733: }
734:
735:
736: static ngx_int_t
737: ngx_http_range_multipart_body(ngx_http_request_t *r,
738: ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
739: {
740: ngx_buf_t *b, *buf;
741: ngx_uint_t i;
742: ngx_chain_t *out, *hcl, *rcl, *dcl, **ll;
743: ngx_http_range_t *range;
744:
745: ll = &out;
746: buf = in->buf;
747: range = ctx->ranges.elts;
748:
749: for (i = 0; i < ctx->ranges.nelts; i++) {
750:
751: /*
752: * The boundary header of the range:
753: * CRLF
754: * "--0123456789" CRLF
755: * "Content-Type: image/jpeg" CRLF
756: * "Content-Range: bytes "
757: */
758:
759: b = ngx_calloc_buf(r->pool);
760: if (b == NULL) {
761: return NGX_ERROR;
762: }
763:
764: b->memory = 1;
765: b->pos = ctx->boundary_header.data;
766: b->last = ctx->boundary_header.data + ctx->boundary_header.len;
767:
768: hcl = ngx_alloc_chain_link(r->pool);
769: if (hcl == NULL) {
770: return NGX_ERROR;
771: }
772:
773: hcl->buf = b;
774:
775:
776: /* "SSSS-EEEE/TTTT" CRLF CRLF */
777:
778: b = ngx_calloc_buf(r->pool);
779: if (b == NULL) {
780: return NGX_ERROR;
781: }
782:
783: b->temporary = 1;
784: b->pos = range[i].content_range.data;
785: b->last = range[i].content_range.data + range[i].content_range.len;
786:
787: rcl = ngx_alloc_chain_link(r->pool);
788: if (rcl == NULL) {
789: return NGX_ERROR;
790: }
791:
792: rcl->buf = b;
793:
794:
795: /* the range data */
796:
797: b = ngx_calloc_buf(r->pool);
798: if (b == NULL) {
799: return NGX_ERROR;
800: }
801:
802: b->in_file = buf->in_file;
803: b->temporary = buf->temporary;
804: b->memory = buf->memory;
805: b->mmap = buf->mmap;
806: b->file = buf->file;
807:
808: if (buf->in_file) {
809: b->file_pos = buf->file_pos + range[i].start;
810: b->file_last = buf->file_pos + range[i].end;
811: }
812:
813: if (ngx_buf_in_memory(buf)) {
814: b->pos = buf->pos + (size_t) range[i].start;
815: b->last = buf->pos + (size_t) range[i].end;
816: }
817:
818: dcl = ngx_alloc_chain_link(r->pool);
819: if (dcl == NULL) {
820: return NGX_ERROR;
821: }
822:
823: dcl->buf = b;
824:
825: *ll = hcl;
826: hcl->next = rcl;
827: rcl->next = dcl;
828: ll = &dcl->next;
829: }
830:
831: /* the last boundary CRLF "--0123456789--" CRLF */
832:
833: b = ngx_calloc_buf(r->pool);
834: if (b == NULL) {
835: return NGX_ERROR;
836: }
837:
838: b->temporary = 1;
839: b->last_buf = 1;
840:
841: b->pos = ngx_pnalloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
842: + sizeof("--" CRLF) - 1);
843: if (b->pos == NULL) {
844: return NGX_ERROR;
845: }
846:
847: b->last = ngx_cpymem(b->pos, ctx->boundary_header.data,
848: sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN);
849: *b->last++ = '-'; *b->last++ = '-';
850: *b->last++ = CR; *b->last++ = LF;
851:
852: hcl = ngx_alloc_chain_link(r->pool);
853: if (hcl == NULL) {
854: return NGX_ERROR;
855: }
856:
857: hcl->buf = b;
858: hcl->next = NULL;
859:
860: *ll = hcl;
861:
862: return ngx_http_next_body_filter(r, out);
863: }
864:
865:
866: static ngx_int_t
867: ngx_http_range_header_filter_init(ngx_conf_t *cf)
868: {
869: ngx_http_next_header_filter = ngx_http_top_header_filter;
870: ngx_http_top_header_filter = ngx_http_range_header_filter;
871:
872: return NGX_OK;
873: }
874:
875:
876: static ngx_int_t
877: ngx_http_range_body_filter_init(ngx_conf_t *cf)
878: {
879: ngx_http_next_body_filter = ngx_http_top_body_filter;
880: ngx_http_top_body_filter = ngx_http_range_body_filter;
881:
882: return NGX_OK;
883: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>