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>