source: nginx/src/http/modules/ngx_http_range_filter_module.c @ 5558:eeb3c2719147

Last change on this file since 5558:eeb3c2719147 was 5558:eeb3c2719147, checked in by Ruslan Ermilov <ru@…>, 4 years ago

Range filter: fixed duplicate charset.

If a proxied response had charset in Content-Type, the
charset was duplicated in a response to client request
with byte ranges.

File size: 23.9 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_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
217parse:
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
254next_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
269static ngx_int_t
270ngx_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
377static ngx_int_t
378ngx_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
421static ngx_int_t
422ngx_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.content_type_len == r->headers_out.content_type.len
436        && r->headers_out.charset.len)
437    {
438        len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
439    }
440
441    ctx->boundary_header.data = ngx_pnalloc(r->pool, len);
442    if (ctx->boundary_header.data == NULL) {
443        return NGX_ERROR;
444    }
445
446    boundary = ngx_next_temp_number(0);
447
448    /*
449     * The boundary header of the range:
450     * CRLF
451     * "--0123456789" CRLF
452     * "Content-Type: image/jpeg" CRLF
453     * "Content-Range: bytes "
454     */
455
456    if (r->headers_out.content_type_len == r->headers_out.content_type.len
457        && r->headers_out.charset.len)
458    {
459        ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
460                                           CRLF "--%0muA" CRLF
461                                           "Content-Type: %V; charset=%V" CRLF
462                                           "Content-Range: bytes ",
463                                           boundary,
464                                           &r->headers_out.content_type,
465                                           &r->headers_out.charset)
466                                   - ctx->boundary_header.data;
467
468    } else if (r->headers_out.content_type.len) {
469        ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
470                                           CRLF "--%0muA" CRLF
471                                           "Content-Type: %V" CRLF
472                                           "Content-Range: bytes ",
473                                           boundary,
474                                           &r->headers_out.content_type)
475                                   - ctx->boundary_header.data;
476
477    } else {
478        ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
479                                           CRLF "--%0muA" CRLF
480                                           "Content-Range: bytes ",
481                                           boundary)
482                                   - ctx->boundary_header.data;
483    }
484
485    r->headers_out.content_type.data =
486        ngx_pnalloc(r->pool,
487                    sizeof("Content-Type: multipart/byteranges; boundary=") - 1
488                    + NGX_ATOMIC_T_LEN);
489
490    if (r->headers_out.content_type.data == NULL) {
491        return NGX_ERROR;
492    }
493
494    r->headers_out.content_type_lowcase = NULL;
495
496    /* "Content-Type: multipart/byteranges; boundary=0123456789" */
497
498    r->headers_out.content_type.len =
499                           ngx_sprintf(r->headers_out.content_type.data,
500                                       "multipart/byteranges; boundary=%0muA",
501                                       boundary)
502                           - r->headers_out.content_type.data;
503
504    r->headers_out.content_type_len = r->headers_out.content_type.len;
505
506    r->headers_out.charset.len = 0;
507
508    /* the size of the last boundary CRLF "--0123456789--" CRLF */
509
510    len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1;
511
512    range = ctx->ranges.elts;
513    for (i = 0; i < ctx->ranges.nelts; i++) {
514
515        /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */
516
517        range[i].content_range.data =
518                               ngx_pnalloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4);
519
520        if (range[i].content_range.data == NULL) {
521            return NGX_ERROR;
522        }
523
524        range[i].content_range.len = ngx_sprintf(range[i].content_range.data,
525                                               "%O-%O/%O" CRLF CRLF,
526                                               range[i].start, range[i].end - 1,
527                                               r->headers_out.content_length_n)
528                                     - range[i].content_range.data;
529
530        len += ctx->boundary_header.len + range[i].content_range.len
531                                    + (size_t) (range[i].end - range[i].start);
532    }
533
534    r->headers_out.content_length_n = len;
535
536    if (r->headers_out.content_length) {
537        r->headers_out.content_length->hash = 0;
538        r->headers_out.content_length = NULL;
539    }
540
541    return ngx_http_next_header_filter(r);
542}
543
544
545static ngx_int_t
546ngx_http_range_not_satisfiable(ngx_http_request_t *r)
547{
548    ngx_table_elt_t  *content_range;
549
550    r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
551
552    content_range = ngx_list_push(&r->headers_out.headers);
553    if (content_range == NULL) {
554        return NGX_ERROR;
555    }
556
557    r->headers_out.content_range = content_range;
558
559    content_range->hash = 1;
560    ngx_str_set(&content_range->key, "Content-Range");
561
562    content_range->value.data = ngx_pnalloc(r->pool,
563                                       sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
564    if (content_range->value.data == NULL) {
565        return NGX_ERROR;
566    }
567
568    content_range->value.len = ngx_sprintf(content_range->value.data,
569                                           "bytes */%O",
570                                           r->headers_out.content_length_n)
571                               - content_range->value.data;
572
573    ngx_http_clear_content_length(r);
574
575    return NGX_HTTP_RANGE_NOT_SATISFIABLE;
576}
577
578
579static ngx_int_t
580ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
581{
582    ngx_http_range_filter_ctx_t  *ctx;
583
584    if (in == NULL) {
585        return ngx_http_next_body_filter(r, in);
586    }
587
588    ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);
589
590    if (ctx == NULL) {
591        return ngx_http_next_body_filter(r, in);
592    }
593
594    if (ctx->ranges.nelts == 1) {
595        return ngx_http_range_singlepart_body(r, ctx, in);
596    }
597
598    /*
599     * multipart ranges are supported only if whole body is in a single buffer
600     */
601
602    if (ngx_buf_special(in->buf)) {
603        return ngx_http_next_body_filter(r, in);
604    }
605
606    if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {
607        return NGX_ERROR;
608    }
609
610    return ngx_http_range_multipart_body(r, ctx, in);
611}
612
613
614static ngx_int_t
615ngx_http_range_test_overlapped(ngx_http_request_t *r,
616    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
617{
618    off_t              start, last;
619    ngx_buf_t         *buf;
620    ngx_uint_t         i;
621    ngx_http_range_t  *range;
622
623    if (ctx->offset) {
624        goto overlapped;
625    }
626
627    buf = in->buf;
628
629    if (!buf->last_buf) {
630        start = ctx->offset;
631        last = ctx->offset + ngx_buf_size(buf);
632
633        range = ctx->ranges.elts;
634        for (i = 0; i < ctx->ranges.nelts; i++) {
635            if (start > range[i].start || last < range[i].end) {
636                 goto overlapped;
637            }
638        }
639    }
640
641    ctx->offset = ngx_buf_size(buf);
642
643    return NGX_OK;
644
645overlapped:
646
647    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
648                  "range in overlapped buffers");
649
650    return NGX_ERROR;
651}
652
653
654static ngx_int_t
655ngx_http_range_singlepart_body(ngx_http_request_t *r,
656    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
657{
658    off_t              start, last;
659    ngx_buf_t         *buf;
660    ngx_chain_t       *out, *cl, **ll;
661    ngx_http_range_t  *range;
662
663    out = NULL;
664    ll = &out;
665    range = ctx->ranges.elts;
666
667    for (cl = in; cl; cl = cl->next) {
668
669        buf = cl->buf;
670
671        start = ctx->offset;
672        last = ctx->offset + ngx_buf_size(buf);
673
674        ctx->offset = last;
675
676        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
677                       "http range body buf: %O-%O", start, last);
678
679        if (ngx_buf_special(buf)) {
680            *ll = cl;
681            ll = &cl->next;
682            continue;
683        }
684
685        if (range->end <= start || range->start >= last) {
686
687            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
688                           "http range body skip");
689
690            if (buf->in_file) {
691                buf->file_pos = buf->file_last;
692            }
693
694            buf->pos = buf->last;
695            buf->sync = 1;
696
697            continue;
698        }
699
700        if (range->start > start) {
701
702            if (buf->in_file) {
703                buf->file_pos += range->start - start;
704            }
705
706            if (ngx_buf_in_memory(buf)) {
707                buf->pos += (size_t) (range->start - start);
708            }
709        }
710
711        if (range->end <= last) {
712
713            if (buf->in_file) {
714                buf->file_last -= last - range->end;
715            }
716
717            if (ngx_buf_in_memory(buf)) {
718                buf->last -= (size_t) (last - range->end);
719            }
720
721            buf->last_buf = 1;
722            *ll = cl;
723            cl->next = NULL;
724
725            break;
726        }
727
728        *ll = cl;
729        ll = &cl->next;
730    }
731
732    if (out == NULL) {
733        return NGX_OK;
734    }
735
736    return ngx_http_next_body_filter(r, out);
737}
738
739
740static ngx_int_t
741ngx_http_range_multipart_body(ngx_http_request_t *r,
742    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
743{
744    ngx_buf_t         *b, *buf;
745    ngx_uint_t         i;
746    ngx_chain_t       *out, *hcl, *rcl, *dcl, **ll;
747    ngx_http_range_t  *range;
748
749    ll = &out;
750    buf = in->buf;
751    range = ctx->ranges.elts;
752
753    for (i = 0; i < ctx->ranges.nelts; i++) {
754
755        /*
756         * The boundary header of the range:
757         * CRLF
758         * "--0123456789" CRLF
759         * "Content-Type: image/jpeg" CRLF
760         * "Content-Range: bytes "
761         */
762
763        b = ngx_calloc_buf(r->pool);
764        if (b == NULL) {
765            return NGX_ERROR;
766        }
767
768        b->memory = 1;
769        b->pos = ctx->boundary_header.data;
770        b->last = ctx->boundary_header.data + ctx->boundary_header.len;
771
772        hcl = ngx_alloc_chain_link(r->pool);
773        if (hcl == NULL) {
774            return NGX_ERROR;
775        }
776
777        hcl->buf = b;
778
779
780        /* "SSSS-EEEE/TTTT" CRLF CRLF */
781
782        b = ngx_calloc_buf(r->pool);
783        if (b == NULL) {
784            return NGX_ERROR;
785        }
786
787        b->temporary = 1;
788        b->pos = range[i].content_range.data;
789        b->last = range[i].content_range.data + range[i].content_range.len;
790
791        rcl = ngx_alloc_chain_link(r->pool);
792        if (rcl == NULL) {
793            return NGX_ERROR;
794        }
795
796        rcl->buf = b;
797
798
799        /* the range data */
800
801        b = ngx_calloc_buf(r->pool);
802        if (b == NULL) {
803            return NGX_ERROR;
804        }
805
806        b->in_file = buf->in_file;
807        b->temporary = buf->temporary;
808        b->memory = buf->memory;
809        b->mmap = buf->mmap;
810        b->file = buf->file;
811
812        if (buf->in_file) {
813            b->file_pos = buf->file_pos + range[i].start;
814            b->file_last = buf->file_pos + range[i].end;
815        }
816
817        if (ngx_buf_in_memory(buf)) {
818            b->pos = buf->pos + (size_t) range[i].start;
819            b->last = buf->pos + (size_t) range[i].end;
820        }
821
822        dcl = ngx_alloc_chain_link(r->pool);
823        if (dcl == NULL) {
824            return NGX_ERROR;
825        }
826
827        dcl->buf = b;
828
829        *ll = hcl;
830        hcl->next = rcl;
831        rcl->next = dcl;
832        ll = &dcl->next;
833    }
834
835    /* the last boundary CRLF "--0123456789--" CRLF  */
836
837    b = ngx_calloc_buf(r->pool);
838    if (b == NULL) {
839        return NGX_ERROR;
840    }
841
842    b->temporary = 1;
843    b->last_buf = 1;
844
845    b->pos = ngx_pnalloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
846                                  + sizeof("--" CRLF) - 1);
847    if (b->pos == NULL) {
848        return NGX_ERROR;
849    }
850
851    b->last = ngx_cpymem(b->pos, ctx->boundary_header.data,
852                         sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN);
853    *b->last++ = '-'; *b->last++ = '-';
854    *b->last++ = CR; *b->last++ = LF;
855
856    hcl = ngx_alloc_chain_link(r->pool);
857    if (hcl == NULL) {
858        return NGX_ERROR;
859    }
860
861    hcl->buf = b;
862    hcl->next = NULL;
863
864    *ll = hcl;
865
866    return ngx_http_next_body_filter(r, out);
867}
868
869
870static ngx_int_t
871ngx_http_range_header_filter_init(ngx_conf_t *cf)
872{
873    ngx_http_next_header_filter = ngx_http_top_header_filter;
874    ngx_http_top_header_filter = ngx_http_range_header_filter;
875
876    return NGX_OK;
877}
878
879
880static ngx_int_t
881ngx_http_range_body_filter_init(ngx_conf_t *cf)
882{
883    ngx_http_next_body_filter = ngx_http_top_body_filter;
884    ngx_http_top_body_filter = ngx_http_range_body_filter;
885
886    return NGX_OK;
887}
Note: See TracBrowser for help on using the repository browser.