Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 /*
0014  * the single part format:
0015  *
0016  * "HTTP/1.0 206 Partial Content" CRLF
0017  * ... header ...
0018  * "Content-Type: image/jpeg" CRLF
0019  * "Content-Length: SIZE" CRLF
0020  * "Content-Range: bytes START-END/SIZE" CRLF
0021  * CRLF
0022  * ... data ...
0023  *
0024  *
0025  * the multipart format:
0026  *
0027  * "HTTP/1.0 206 Partial Content" CRLF
0028  * ... header ...
0029  * "Content-Type: multipart/byteranges; boundary=0123456789" CRLF
0030  * CRLF
0031  * CRLF
0032  * "--0123456789" CRLF
0033  * "Content-Type: image/jpeg" CRLF
0034  * "Content-Range: bytes START0-END0/SIZE" CRLF
0035  * CRLF
0036  * ... data ...
0037  * CRLF
0038  * "--0123456789" CRLF
0039  * "Content-Type: image/jpeg" CRLF
0040  * "Content-Range: bytes START1-END1/SIZE" CRLF
0041  * CRLF
0042  * ... data ...
0043  * CRLF
0044  * "--0123456789--" CRLF
0045  */
0046 
0047 
0048 typedef struct {
0049     off_t        start;
0050     off_t        end;
0051     ngx_str_t    content_range;
0052 } ngx_http_range_t;
0053 
0054 
0055 typedef struct {
0056     off_t        offset;
0057     ngx_str_t    boundary_header;
0058     ngx_array_t  ranges;
0059 } ngx_http_range_filter_ctx_t;
0060 
0061 
0062 static ngx_int_t ngx_http_range_parse(ngx_http_request_t *r,
0063     ngx_http_range_filter_ctx_t *ctx, ngx_uint_t ranges);
0064 static ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r,
0065     ngx_http_range_filter_ctx_t *ctx);
0066 static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r,
0067     ngx_http_range_filter_ctx_t *ctx);
0068 static ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r);
0069 static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r,
0070     ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
0071 static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r,
0072     ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
0073 static ngx_int_t ngx_http_range_multipart_body(ngx_http_request_t *r,
0074     ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
0075 
0076 static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf);
0077 static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf);
0078 
0079 
0080 static ngx_http_module_t  ngx_http_range_header_filter_module_ctx = {
0081     NULL,                                  /* preconfiguration */
0082     ngx_http_range_header_filter_init,     /* postconfiguration */
0083 
0084     NULL,                                  /* create main configuration */
0085     NULL,                                  /* init main configuration */
0086 
0087     NULL,                                  /* create server configuration */
0088     NULL,                                  /* merge server configuration */
0089 
0090     NULL,                                  /* create location configuration */
0091     NULL,                                  /* merge location configuration */
0092 };
0093 
0094 
0095 ngx_module_t  ngx_http_range_header_filter_module = {
0096     NGX_MODULE_V1,
0097     &ngx_http_range_header_filter_module_ctx, /* module context */
0098     NULL,                                  /* module directives */
0099     NGX_HTTP_MODULE,                       /* module type */
0100     NULL,                                  /* init master */
0101     NULL,                                  /* init module */
0102     NULL,                                  /* init process */
0103     NULL,                                  /* init thread */
0104     NULL,                                  /* exit thread */
0105     NULL,                                  /* exit process */
0106     NULL,                                  /* exit master */
0107     NGX_MODULE_V1_PADDING
0108 };
0109 
0110 
0111 static ngx_http_module_t  ngx_http_range_body_filter_module_ctx = {
0112     NULL,                                  /* preconfiguration */
0113     ngx_http_range_body_filter_init,       /* postconfiguration */
0114 
0115     NULL,                                  /* create main configuration */
0116     NULL,                                  /* init main configuration */
0117 
0118     NULL,                                  /* create server configuration */
0119     NULL,                                  /* merge server configuration */
0120 
0121     NULL,                                  /* create location configuration */
0122     NULL,                                  /* merge location configuration */
0123 };
0124 
0125 
0126 ngx_module_t  ngx_http_range_body_filter_module = {
0127     NGX_MODULE_V1,
0128     &ngx_http_range_body_filter_module_ctx, /* module context */
0129     NULL,                                  /* module directives */
0130     NGX_HTTP_MODULE,                       /* module type */
0131     NULL,                                  /* init master */
0132     NULL,                                  /* init module */
0133     NULL,                                  /* init process */
0134     NULL,                                  /* init thread */
0135     NULL,                                  /* exit thread */
0136     NULL,                                  /* exit process */
0137     NULL,                                  /* exit master */
0138     NGX_MODULE_V1_PADDING
0139 };
0140 
0141 
0142 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
0143 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
0144 
0145 
0146 static ngx_int_t
0147 ngx_http_range_header_filter(ngx_http_request_t *r)
0148 {
0149     time_t                        if_range_time;
0150     ngx_str_t                    *if_range, *etag;
0151     ngx_uint_t                    ranges;
0152     ngx_http_core_loc_conf_t     *clcf;
0153     ngx_http_range_filter_ctx_t  *ctx;
0154 
0155     if (r->http_version < NGX_HTTP_VERSION_10
0156         || r->headers_out.status != NGX_HTTP_OK
0157         || (r != r->main && !r->subrequest_ranges)
0158         || r->headers_out.content_length_n == -1
0159         || !r->allow_ranges)
0160     {
0161         return ngx_http_next_header_filter(r);
0162     }
0163 
0164     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
0165 
0166     if (clcf->max_ranges == 0) {
0167         return ngx_http_next_header_filter(r);
0168     }
0169 
0170     if (r->headers_in.range == NULL
0171         || r->headers_in.range->value.len < 7
0172         || ngx_strncasecmp(r->headers_in.range->value.data,
0173                            (u_char *) "bytes=", 6)
0174            != 0)
0175     {
0176         goto next_filter;
0177     }
0178 
0179     if (r->headers_in.if_range) {
0180 
0181         if_range = &r->headers_in.if_range->value;
0182 
0183         if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '"') {
0184 
0185             if (r->headers_out.etag == NULL) {
0186                 goto next_filter;
0187             }
0188 
0189             etag = &r->headers_out.etag->value;
0190 
0191             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0192                            "http ir:%V etag:%V", if_range, etag);
0193 
0194             if (if_range->len != etag->len
0195                 || ngx_strncmp(if_range->data, etag->data, etag->len) != 0)
0196             {
0197                 goto next_filter;
0198             }
0199 
0200             goto parse;
0201         }
0202 
0203         if (r->headers_out.last_modified_time == (time_t) -1) {
0204             goto next_filter;
0205         }
0206 
0207         if_range_time = ngx_parse_http_time(if_range->data, if_range->len);
0208 
0209         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0210                        "http ir:%T lm:%T",
0211                        if_range_time, r->headers_out.last_modified_time);
0212 
0213         if (if_range_time != r->headers_out.last_modified_time) {
0214             goto next_filter;
0215         }
0216     }
0217 
0218 parse:
0219 
0220     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
0221     if (ctx == NULL) {
0222         return NGX_ERROR;
0223     }
0224 
0225     ctx->offset = r->headers_out.content_offset;
0226 
0227     ranges = r->single_range ? 1 : clcf->max_ranges;
0228 
0229     switch (ngx_http_range_parse(r, ctx, ranges)) {
0230 
0231     case NGX_OK:
0232         ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
0233 
0234         r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
0235         r->headers_out.status_line.len = 0;
0236 
0237         if (ctx->ranges.nelts == 1) {
0238             return ngx_http_range_singlepart_header(r, ctx);
0239         }
0240 
0241         return ngx_http_range_multipart_header(r, ctx);
0242 
0243     case NGX_HTTP_RANGE_NOT_SATISFIABLE:
0244         return ngx_http_range_not_satisfiable(r);
0245 
0246     case NGX_ERROR:
0247         return NGX_ERROR;
0248 
0249     default: /* NGX_DECLINED */
0250         break;
0251     }
0252 
0253 next_filter:
0254 
0255     r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
0256     if (r->headers_out.accept_ranges == NULL) {
0257         return NGX_ERROR;
0258     }
0259 
0260     r->headers_out.accept_ranges->hash = 1;
0261     ngx_str_set(&r->headers_out.accept_ranges->key, "Accept-Ranges");
0262     ngx_str_set(&r->headers_out.accept_ranges->value, "bytes");
0263 
0264     return ngx_http_next_header_filter(r);
0265 }
0266 
0267 
0268 static ngx_int_t
0269 ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx,
0270     ngx_uint_t ranges)
0271 {
0272     u_char                       *p;
0273     off_t                         start, end, size, content_length, cutoff,
0274                                   cutlim;
0275     ngx_uint_t                    suffix;
0276     ngx_http_range_t             *range;
0277     ngx_http_range_filter_ctx_t  *mctx;
0278 
0279     if (r != r->main) {
0280         mctx = ngx_http_get_module_ctx(r->main,
0281                                        ngx_http_range_body_filter_module);
0282         if (mctx) {
0283             ctx->ranges = mctx->ranges;
0284             return NGX_OK;
0285         }
0286     }
0287 
0288     if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))
0289         != NGX_OK)
0290     {
0291         return NGX_ERROR;
0292     }
0293 
0294     p = r->headers_in.range->value.data + 6;
0295     size = 0;
0296     content_length = r->headers_out.content_length_n;
0297 
0298     cutoff = NGX_MAX_OFF_T_VALUE / 10;
0299     cutlim = NGX_MAX_OFF_T_VALUE % 10;
0300 
0301     for ( ;; ) {
0302         start = 0;
0303         end = 0;
0304         suffix = 0;
0305 
0306         while (*p == ' ') { p++; }
0307 
0308         if (*p != '-') {
0309             if (*p < '0' || *p > '9') {
0310                 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
0311             }
0312 
0313             while (*p >= '0' && *p <= '9') {
0314                 if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {
0315                     return NGX_HTTP_RANGE_NOT_SATISFIABLE;
0316                 }
0317 
0318                 start = start * 10 + (*p++ - '0');
0319             }
0320 
0321             while (*p == ' ') { p++; }
0322 
0323             if (*p++ != '-') {
0324                 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
0325             }
0326 
0327             while (*p == ' ') { p++; }
0328 
0329             if (*p == ',' || *p == '\0') {
0330                 end = content_length;
0331                 goto found;
0332             }
0333 
0334         } else {
0335             suffix = 1;
0336             p++;
0337         }
0338 
0339         if (*p < '0' || *p > '9') {
0340             return NGX_HTTP_RANGE_NOT_SATISFIABLE;
0341         }
0342 
0343         while (*p >= '0' && *p <= '9') {
0344             if (end >= cutoff && (end > cutoff || *p - '0' > cutlim)) {
0345                 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
0346             }
0347 
0348             end = end * 10 + (*p++ - '0');
0349         }
0350 
0351         while (*p == ' ') { p++; }
0352 
0353         if (*p != ',' && *p != '\0') {
0354             return NGX_HTTP_RANGE_NOT_SATISFIABLE;
0355         }
0356 
0357         if (suffix) {
0358             start = (end < content_length) ? content_length - end : 0;
0359             end = content_length - 1;
0360         }
0361 
0362         if (end >= content_length) {
0363             end = content_length;
0364 
0365         } else {
0366             end++;
0367         }
0368 
0369     found:
0370 
0371         if (start < end) {
0372             range = ngx_array_push(&ctx->ranges);
0373             if (range == NULL) {
0374                 return NGX_ERROR;
0375             }
0376 
0377             range->start = start;
0378             range->end = end;
0379 
0380             if (size > NGX_MAX_OFF_T_VALUE - (end - start)) {
0381                 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
0382             }
0383 
0384             size += end - start;
0385 
0386             if (ranges-- == 0) {
0387                 return NGX_DECLINED;
0388             }
0389 
0390         } else if (start == 0) {
0391             return NGX_DECLINED;
0392         }
0393 
0394         if (*p++ != ',') {
0395             break;
0396         }
0397     }
0398 
0399     if (ctx->ranges.nelts == 0) {
0400         return NGX_HTTP_RANGE_NOT_SATISFIABLE;
0401     }
0402 
0403     if (size > content_length) {
0404         return NGX_DECLINED;
0405     }
0406 
0407     return NGX_OK;
0408 }
0409 
0410 
0411 static ngx_int_t
0412 ngx_http_range_singlepart_header(ngx_http_request_t *r,
0413     ngx_http_range_filter_ctx_t *ctx)
0414 {
0415     ngx_table_elt_t   *content_range;
0416     ngx_http_range_t  *range;
0417 
0418     if (r != r->main) {
0419         return ngx_http_next_header_filter(r);
0420     }
0421 
0422     content_range = ngx_list_push(&r->headers_out.headers);
0423     if (content_range == NULL) {
0424         return NGX_ERROR;
0425     }
0426 
0427     r->headers_out.content_range = content_range;
0428 
0429     content_range->hash = 1;
0430     ngx_str_set(&content_range->key, "Content-Range");
0431 
0432     content_range->value.data = ngx_pnalloc(r->pool,
0433                                     sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
0434     if (content_range->value.data == NULL) {
0435         content_range->hash = 0;
0436         r->headers_out.content_range = NULL;
0437         return NGX_ERROR;
0438     }
0439 
0440     /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
0441 
0442     range = ctx->ranges.elts;
0443 
0444     content_range->value.len = ngx_sprintf(content_range->value.data,
0445                                            "bytes %O-%O/%O",
0446                                            range->start, range->end - 1,
0447                                            r->headers_out.content_length_n)
0448                                - content_range->value.data;
0449 
0450     r->headers_out.content_length_n = range->end - range->start;
0451     r->headers_out.content_offset = range->start;
0452 
0453     if (r->headers_out.content_length) {
0454         r->headers_out.content_length->hash = 0;
0455         r->headers_out.content_length = NULL;
0456     }
0457 
0458     return ngx_http_next_header_filter(r);
0459 }
0460 
0461 
0462 static ngx_int_t
0463 ngx_http_range_multipart_header(ngx_http_request_t *r,
0464     ngx_http_range_filter_ctx_t *ctx)
0465 {
0466     off_t               len;
0467     size_t              size;
0468     ngx_uint_t          i;
0469     ngx_http_range_t   *range;
0470     ngx_atomic_uint_t   boundary;
0471 
0472     size = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
0473            + sizeof(CRLF "Content-Type: ") - 1
0474            + r->headers_out.content_type.len
0475            + sizeof(CRLF "Content-Range: bytes ") - 1;
0476 
0477     if (r->headers_out.content_type_len == r->headers_out.content_type.len
0478         && r->headers_out.charset.len)
0479     {
0480         size += sizeof("; charset=") - 1 + r->headers_out.charset.len;
0481     }
0482 
0483     ctx->boundary_header.data = ngx_pnalloc(r->pool, size);
0484     if (ctx->boundary_header.data == NULL) {
0485         return NGX_ERROR;
0486     }
0487 
0488     boundary = ngx_next_temp_number(0);
0489 
0490     /*
0491      * The boundary header of the range:
0492      * CRLF
0493      * "--0123456789" CRLF
0494      * "Content-Type: image/jpeg" CRLF
0495      * "Content-Range: bytes "
0496      */
0497 
0498     if (r->headers_out.content_type_len == r->headers_out.content_type.len
0499         && r->headers_out.charset.len)
0500     {
0501         ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
0502                                            CRLF "--%0muA" CRLF
0503                                            "Content-Type: %V; charset=%V" CRLF
0504                                            "Content-Range: bytes ",
0505                                            boundary,
0506                                            &r->headers_out.content_type,
0507                                            &r->headers_out.charset)
0508                                    - ctx->boundary_header.data;
0509 
0510     } else if (r->headers_out.content_type.len) {
0511         ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
0512                                            CRLF "--%0muA" CRLF
0513                                            "Content-Type: %V" CRLF
0514                                            "Content-Range: bytes ",
0515                                            boundary,
0516                                            &r->headers_out.content_type)
0517                                    - ctx->boundary_header.data;
0518 
0519     } else {
0520         ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
0521                                            CRLF "--%0muA" CRLF
0522                                            "Content-Range: bytes ",
0523                                            boundary)
0524                                    - ctx->boundary_header.data;
0525     }
0526 
0527     r->headers_out.content_type.data =
0528         ngx_pnalloc(r->pool,
0529                     sizeof("Content-Type: multipart/byteranges; boundary=") - 1
0530                     + NGX_ATOMIC_T_LEN);
0531 
0532     if (r->headers_out.content_type.data == NULL) {
0533         return NGX_ERROR;
0534     }
0535 
0536     r->headers_out.content_type_lowcase = NULL;
0537 
0538     /* "Content-Type: multipart/byteranges; boundary=0123456789" */
0539 
0540     r->headers_out.content_type.len =
0541                            ngx_sprintf(r->headers_out.content_type.data,
0542                                        "multipart/byteranges; boundary=%0muA",
0543                                        boundary)
0544                            - r->headers_out.content_type.data;
0545 
0546     r->headers_out.content_type_len = r->headers_out.content_type.len;
0547 
0548     r->headers_out.charset.len = 0;
0549 
0550     /* the size of the last boundary CRLF "--0123456789--" CRLF */
0551 
0552     len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1;
0553 
0554     range = ctx->ranges.elts;
0555     for (i = 0; i < ctx->ranges.nelts; i++) {
0556 
0557         /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */
0558 
0559         range[i].content_range.data =
0560                                ngx_pnalloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4);
0561 
0562         if (range[i].content_range.data == NULL) {
0563             return NGX_ERROR;
0564         }
0565 
0566         range[i].content_range.len = ngx_sprintf(range[i].content_range.data,
0567                                                "%O-%O/%O" CRLF CRLF,
0568                                                range[i].start, range[i].end - 1,
0569                                                r->headers_out.content_length_n)
0570                                      - range[i].content_range.data;
0571 
0572         len += ctx->boundary_header.len + range[i].content_range.len
0573                                              + (range[i].end - range[i].start);
0574     }
0575 
0576     r->headers_out.content_length_n = len;
0577 
0578     if (r->headers_out.content_length) {
0579         r->headers_out.content_length->hash = 0;
0580         r->headers_out.content_length = NULL;
0581     }
0582 
0583     return ngx_http_next_header_filter(r);
0584 }
0585 
0586 
0587 static ngx_int_t
0588 ngx_http_range_not_satisfiable(ngx_http_request_t *r)
0589 {
0590     ngx_table_elt_t  *content_range;
0591 
0592     r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
0593 
0594     content_range = ngx_list_push(&r->headers_out.headers);
0595     if (content_range == NULL) {
0596         return NGX_ERROR;
0597     }
0598 
0599     r->headers_out.content_range = content_range;
0600 
0601     content_range->hash = 1;
0602     ngx_str_set(&content_range->key, "Content-Range");
0603 
0604     content_range->value.data = ngx_pnalloc(r->pool,
0605                                        sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
0606     if (content_range->value.data == NULL) {
0607         content_range->hash = 0;
0608         r->headers_out.content_range = NULL;
0609         return NGX_ERROR;
0610     }
0611 
0612     content_range->value.len = ngx_sprintf(content_range->value.data,
0613                                            "bytes */%O",
0614                                            r->headers_out.content_length_n)
0615                                - content_range->value.data;
0616 
0617     ngx_http_clear_content_length(r);
0618 
0619     return NGX_HTTP_RANGE_NOT_SATISFIABLE;
0620 }
0621 
0622 
0623 static ngx_int_t
0624 ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
0625 {
0626     ngx_http_range_filter_ctx_t  *ctx;
0627 
0628     if (in == NULL) {
0629         return ngx_http_next_body_filter(r, in);
0630     }
0631 
0632     ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);
0633 
0634     if (ctx == NULL) {
0635         return ngx_http_next_body_filter(r, in);
0636     }
0637 
0638     if (ctx->ranges.nelts == 1) {
0639         return ngx_http_range_singlepart_body(r, ctx, in);
0640     }
0641 
0642     /*
0643      * multipart ranges are supported only if whole body is in a single buffer
0644      */
0645 
0646     if (ngx_buf_special(in->buf)) {
0647         return ngx_http_next_body_filter(r, in);
0648     }
0649 
0650     if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {
0651         return NGX_ERROR;
0652     }
0653 
0654     return ngx_http_range_multipart_body(r, ctx, in);
0655 }
0656 
0657 
0658 static ngx_int_t
0659 ngx_http_range_test_overlapped(ngx_http_request_t *r,
0660     ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
0661 {
0662     off_t              start, last;
0663     ngx_buf_t         *buf;
0664     ngx_uint_t         i;
0665     ngx_http_range_t  *range;
0666 
0667     if (ctx->offset) {
0668         goto overlapped;
0669     }
0670 
0671     buf = in->buf;
0672 
0673     if (!buf->last_buf) {
0674         start = ctx->offset;
0675         last = ctx->offset + ngx_buf_size(buf);
0676 
0677         range = ctx->ranges.elts;
0678         for (i = 0; i < ctx->ranges.nelts; i++) {
0679             if (start > range[i].start || last < range[i].end) {
0680                 goto overlapped;
0681             }
0682         }
0683     }
0684 
0685     ctx->offset = ngx_buf_size(buf);
0686 
0687     return NGX_OK;
0688 
0689 overlapped:
0690 
0691     ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
0692                   "range in overlapped buffers");
0693 
0694     return NGX_ERROR;
0695 }
0696 
0697 
0698 static ngx_int_t
0699 ngx_http_range_singlepart_body(ngx_http_request_t *r,
0700     ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
0701 {
0702     off_t              start, last;
0703     ngx_buf_t         *buf;
0704     ngx_chain_t       *out, *cl, **ll;
0705     ngx_http_range_t  *range;
0706 
0707     out = NULL;
0708     ll = &out;
0709     range = ctx->ranges.elts;
0710 
0711     for (cl = in; cl; cl = cl->next) {
0712 
0713         buf = cl->buf;
0714 
0715         start = ctx->offset;
0716         last = ctx->offset + ngx_buf_size(buf);
0717 
0718         ctx->offset = last;
0719 
0720         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0721                        "http range body buf: %O-%O", start, last);
0722 
0723         if (ngx_buf_special(buf)) {
0724             *ll = cl;
0725             ll = &cl->next;
0726             continue;
0727         }
0728 
0729         if (range->end <= start || range->start >= last) {
0730 
0731             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0732                            "http range body skip");
0733 
0734             if (buf->in_file) {
0735                 buf->file_pos = buf->file_last;
0736             }
0737 
0738             buf->pos = buf->last;
0739             buf->sync = 1;
0740 
0741             continue;
0742         }
0743 
0744         if (range->start > start) {
0745 
0746             if (buf->in_file) {
0747                 buf->file_pos += range->start - start;
0748             }
0749 
0750             if (ngx_buf_in_memory(buf)) {
0751                 buf->pos += (size_t) (range->start - start);
0752             }
0753         }
0754 
0755         if (range->end <= last) {
0756 
0757             if (buf->in_file) {
0758                 buf->file_last -= last - range->end;
0759             }
0760 
0761             if (ngx_buf_in_memory(buf)) {
0762                 buf->last -= (size_t) (last - range->end);
0763             }
0764 
0765             buf->last_buf = (r == r->main) ? 1 : 0;
0766             buf->last_in_chain = 1;
0767             *ll = cl;
0768             cl->next = NULL;
0769 
0770             break;
0771         }
0772 
0773         *ll = cl;
0774         ll = &cl->next;
0775     }
0776 
0777     if (out == NULL) {
0778         return NGX_OK;
0779     }
0780 
0781     return ngx_http_next_body_filter(r, out);
0782 }
0783 
0784 
0785 static ngx_int_t
0786 ngx_http_range_multipart_body(ngx_http_request_t *r,
0787     ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
0788 {
0789     ngx_buf_t         *b, *buf;
0790     ngx_uint_t         i;
0791     ngx_chain_t       *out, *hcl, *rcl, *dcl, **ll;
0792     ngx_http_range_t  *range;
0793 
0794     ll = &out;
0795     buf = in->buf;
0796     range = ctx->ranges.elts;
0797 
0798     for (i = 0; i < ctx->ranges.nelts; i++) {
0799 
0800         /*
0801          * The boundary header of the range:
0802          * CRLF
0803          * "--0123456789" CRLF
0804          * "Content-Type: image/jpeg" CRLF
0805          * "Content-Range: bytes "
0806          */
0807 
0808         b = ngx_calloc_buf(r->pool);
0809         if (b == NULL) {
0810             return NGX_ERROR;
0811         }
0812 
0813         b->memory = 1;
0814         b->pos = ctx->boundary_header.data;
0815         b->last = ctx->boundary_header.data + ctx->boundary_header.len;
0816 
0817         hcl = ngx_alloc_chain_link(r->pool);
0818         if (hcl == NULL) {
0819             return NGX_ERROR;
0820         }
0821 
0822         hcl->buf = b;
0823 
0824 
0825         /* "SSSS-EEEE/TTTT" CRLF CRLF */
0826 
0827         b = ngx_calloc_buf(r->pool);
0828         if (b == NULL) {
0829             return NGX_ERROR;
0830         }
0831 
0832         b->temporary = 1;
0833         b->pos = range[i].content_range.data;
0834         b->last = range[i].content_range.data + range[i].content_range.len;
0835 
0836         rcl = ngx_alloc_chain_link(r->pool);
0837         if (rcl == NULL) {
0838             return NGX_ERROR;
0839         }
0840 
0841         rcl->buf = b;
0842 
0843 
0844         /* the range data */
0845 
0846         b = ngx_calloc_buf(r->pool);
0847         if (b == NULL) {
0848             return NGX_ERROR;
0849         }
0850 
0851         b->in_file = buf->in_file;
0852         b->temporary = buf->temporary;
0853         b->memory = buf->memory;
0854         b->mmap = buf->mmap;
0855         b->file = buf->file;
0856 
0857         if (buf->in_file) {
0858             b->file_pos = buf->file_pos + range[i].start;
0859             b->file_last = buf->file_pos + range[i].end;
0860         }
0861 
0862         if (ngx_buf_in_memory(buf)) {
0863             b->pos = buf->pos + (size_t) range[i].start;
0864             b->last = buf->pos + (size_t) range[i].end;
0865         }
0866 
0867         dcl = ngx_alloc_chain_link(r->pool);
0868         if (dcl == NULL) {
0869             return NGX_ERROR;
0870         }
0871 
0872         dcl->buf = b;
0873 
0874         *ll = hcl;
0875         hcl->next = rcl;
0876         rcl->next = dcl;
0877         ll = &dcl->next;
0878     }
0879 
0880     /* the last boundary CRLF "--0123456789--" CRLF  */
0881 
0882     b = ngx_calloc_buf(r->pool);
0883     if (b == NULL) {
0884         return NGX_ERROR;
0885     }
0886 
0887     b->temporary = 1;
0888     b->last_buf = 1;
0889 
0890     b->pos = ngx_pnalloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
0891                                   + sizeof("--" CRLF) - 1);
0892     if (b->pos == NULL) {
0893         return NGX_ERROR;
0894     }
0895 
0896     b->last = ngx_cpymem(b->pos, ctx->boundary_header.data,
0897                          sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN);
0898     *b->last++ = '-'; *b->last++ = '-';
0899     *b->last++ = CR; *b->last++ = LF;
0900 
0901     hcl = ngx_alloc_chain_link(r->pool);
0902     if (hcl == NULL) {
0903         return NGX_ERROR;
0904     }
0905 
0906     hcl->buf = b;
0907     hcl->next = NULL;
0908 
0909     *ll = hcl;
0910 
0911     return ngx_http_next_body_filter(r, out);
0912 }
0913 
0914 
0915 static ngx_int_t
0916 ngx_http_range_header_filter_init(ngx_conf_t *cf)
0917 {
0918     ngx_http_next_header_filter = ngx_http_top_header_filter;
0919     ngx_http_top_header_filter = ngx_http_range_header_filter;
0920 
0921     return NGX_OK;
0922 }
0923 
0924 
0925 static ngx_int_t
0926 ngx_http_range_body_filter_init(ngx_conf_t *cf)
0927 {
0928     ngx_http_next_body_filter = ngx_http_top_body_filter;
0929     ngx_http_top_body_filter = ngx_http_range_body_filter;
0930 
0931     return NGX_OK;
0932 }