source: nginx/src/http/modules/ngx_http_range_filter_module.c @ 5621:345e4fd4bb64

Last change on this file since 5621:345e4fd4bb64 was 5621:345e4fd4bb64, checked in by Maxim Dounin <mdounin@…>, 4 years ago

Range filter: single_range flag in request.

If set, it means that response body is going to be in more than one buffer,
hence only range requests with a single range should be honored.

The flag is now used by mp4 and cacheable upstream responses, thus allowing
range requests of mp4 files with start/end, as well as range processing
on a first request to a not-yet-cached files with proxy_cache.

Notably this makes it possible to play mp4 files (with proxy_cache, or with
mp4 module) on iOS devices, as byte-range support is required by Apple.

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