Mercurial > hg > nginx-ranges
comparison src/http/modules/ngx_http_range_filter_module.c @ 272:29a6403156b0 NGINX_0_5_6
nginx 0.5.6
*) Change: now the ngx_http_index_module ignores all methods except the
GET, HEAD, and POST methods.
*) Feature: the ngx_http_limit_zone_module.
*) Feature: the $binary_remote_addr variable.
*) Feature: the "ssl_session_cache" directives of the
ngx_http_ssl_module and ngx_imap_ssl_module.
*) Feature: the DELETE method supports recursive removal.
*) Bugfix: the byte-ranges were transferred incorrectly if the
$r->sendfile() was used.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Tue, 09 Jan 2007 00:00:00 +0300 |
parents | 38e7b94d63ac |
children | 675a39fd14cd |
comparison
equal
deleted
inserted
replaced
271:fcbee7dacf2b | 272:29a6403156b0 |
---|---|
43 * "--0123456789--" CRLF | 43 * "--0123456789--" CRLF |
44 */ | 44 */ |
45 | 45 |
46 | 46 |
47 typedef struct { | 47 typedef struct { |
48 ngx_str_t boundary_header; | 48 off_t start; |
49 off_t end; | |
50 ngx_str_t content_range; | |
51 } ngx_http_range_t; | |
52 | |
53 | |
54 typedef struct { | |
55 off_t offset; | |
56 ngx_str_t boundary_header; | |
57 ngx_array_t ranges; | |
49 } ngx_http_range_filter_ctx_t; | 58 } ngx_http_range_filter_ctx_t; |
50 | 59 |
51 | 60 |
52 static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf); | 61 static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf); |
53 static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf); | 62 static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf); |
157 r->headers_out.accept_ranges->value.data = (u_char *) "bytes"; | 166 r->headers_out.accept_ranges->value.data = (u_char *) "bytes"; |
158 | 167 |
159 return ngx_http_next_header_filter(r); | 168 return ngx_http_next_header_filter(r); |
160 } | 169 } |
161 | 170 |
162 if (ngx_array_init(&r->headers_out.ranges, r->pool, 2, | 171 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t)); |
163 sizeof(ngx_http_range_t)) == NGX_ERROR) | 172 if (ctx == NULL) { |
173 return NGX_ERROR; | |
174 } | |
175 | |
176 if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t)) | |
177 == NGX_ERROR) | |
164 { | 178 { |
165 return NGX_ERROR; | 179 return NGX_ERROR; |
166 } | 180 } |
167 | 181 |
168 rc = 0; | 182 rc = 0; |
199 } | 213 } |
200 | 214 |
201 while (*p == ' ') { p++; } | 215 while (*p == ' ') { p++; } |
202 | 216 |
203 if (*p == ',' || *p == '\0') { | 217 if (*p == ',' || *p == '\0') { |
204 range = ngx_array_push(&r->headers_out.ranges); | 218 range = ngx_array_push(&ctx->ranges); |
205 if (range == NULL) { | 219 if (range == NULL) { |
206 return NGX_ERROR; | 220 return NGX_ERROR; |
207 } | 221 } |
208 | 222 |
209 range->start = start; | 223 range->start = start; |
245 if (start > end) { | 259 if (start > end) { |
246 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | 260 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; |
247 break; | 261 break; |
248 } | 262 } |
249 | 263 |
250 range = ngx_array_push(&r->headers_out.ranges); | 264 range = ngx_array_push(&ctx->ranges); |
251 if (range == NULL) { | 265 if (range == NULL) { |
252 return NGX_ERROR; | 266 return NGX_ERROR; |
253 } | 267 } |
254 | 268 |
255 range->start = start; | 269 range->start = start; |
273 if (rc) { | 287 if (rc) { |
274 | 288 |
275 /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */ | 289 /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */ |
276 | 290 |
277 r->headers_out.status = rc; | 291 r->headers_out.status = rc; |
278 r->headers_out.ranges.nelts = 0; | |
279 | 292 |
280 content_range = ngx_list_push(&r->headers_out.headers); | 293 content_range = ngx_list_push(&r->headers_out.headers); |
281 if (content_range == NULL) { | 294 if (content_range == NULL) { |
282 return NGX_ERROR; | 295 return NGX_ERROR; |
283 } | 296 } |
302 ngx_http_clear_content_length(r); | 315 ngx_http_clear_content_length(r); |
303 | 316 |
304 return rc; | 317 return rc; |
305 } | 318 } |
306 | 319 |
320 ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); | |
321 | |
307 r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; | 322 r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; |
308 | 323 |
309 if (r->headers_out.ranges.nelts == 1) { | 324 if (ctx->ranges.nelts == 1) { |
310 | 325 |
311 content_range = ngx_list_push(&r->headers_out.headers); | 326 content_range = ngx_list_push(&r->headers_out.headers); |
312 if (content_range == NULL) { | 327 if (content_range == NULL) { |
313 return NGX_ERROR; | 328 return NGX_ERROR; |
314 } | 329 } |
333 r->headers_out.content_length_n) | 348 r->headers_out.content_length_n) |
334 - content_range->value.data; | 349 - content_range->value.data; |
335 | 350 |
336 r->headers_out.content_length_n = range->end - range->start; | 351 r->headers_out.content_length_n = range->end - range->start; |
337 | 352 |
353 if (r->headers_out.content_length) { | |
354 r->headers_out.content_length->hash = 0; | |
355 r->headers_out.content_length = NULL; | |
356 } | |
357 | |
338 return ngx_http_next_header_filter(r); | 358 return ngx_http_next_header_filter(r); |
339 } | 359 } |
340 | 360 |
341 | 361 |
342 /* TODO: what if no content_type ?? */ | 362 /* TODO: what if no content_type ?? */ |
343 | |
344 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t)); | |
345 if (ctx == NULL) { | |
346 return NGX_ERROR; | |
347 } | |
348 | |
349 ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); | |
350 | |
351 | 363 |
352 len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN | 364 len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN |
353 + sizeof(CRLF "Content-Type: ") - 1 | 365 + sizeof(CRLF "Content-Type: ") - 1 |
354 + r->headers_out.content_type.len | 366 + r->headers_out.content_type.len |
355 + sizeof(CRLF "Content-Range: bytes ") - 1; | 367 + sizeof(CRLF "Content-Range: bytes ") - 1; |
415 | 427 |
416 /* the size of the last boundary CRLF "--0123456789--" CRLF */ | 428 /* the size of the last boundary CRLF "--0123456789--" CRLF */ |
417 | 429 |
418 len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1; | 430 len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1; |
419 | 431 |
420 range = r->headers_out.ranges.elts; | 432 range = ctx->ranges.elts; |
421 for (i = 0; i < r->headers_out.ranges.nelts; i++) { | 433 for (i = 0; i < ctx->ranges.nelts; i++) { |
422 | 434 |
423 /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */ | 435 /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */ |
424 | 436 |
425 range[i].content_range.data = | 437 range[i].content_range.data = |
426 ngx_palloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4); | 438 ngx_palloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4); |
438 len += ctx->boundary_header.len + range[i].content_range.len | 450 len += ctx->boundary_header.len + range[i].content_range.len |
439 + (size_t) (range[i].end - range[i].start); | 451 + (size_t) (range[i].end - range[i].start); |
440 } | 452 } |
441 | 453 |
442 r->headers_out.content_length_n = len; | 454 r->headers_out.content_length_n = len; |
443 r->headers_out.content_length = NULL; | 455 |
456 if (r->headers_out.content_length) { | |
457 r->headers_out.content_length->hash = 0; | |
458 r->headers_out.content_length = NULL; | |
459 } | |
444 | 460 |
445 return ngx_http_next_header_filter(r); | 461 return ngx_http_next_header_filter(r); |
446 } | 462 } |
447 | 463 |
448 | 464 |
449 static ngx_int_t | 465 static ngx_int_t |
450 ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in) | 466 ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in) |
451 { | 467 { |
468 off_t start, last; | |
469 ngx_buf_t *b, *buf; | |
452 ngx_uint_t i; | 470 ngx_uint_t i; |
453 ngx_buf_t *b; | |
454 ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; | 471 ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; |
455 ngx_http_range_t *range; | 472 ngx_http_range_t *range; |
456 ngx_http_range_filter_ctx_t *ctx; | 473 ngx_http_range_filter_ctx_t *ctx; |
457 | 474 |
458 if (r->headers_out.ranges.nelts == 0) { | 475 if (in == NULL) { |
459 return ngx_http_next_body_filter(r, in); | 476 return ngx_http_next_body_filter(r, in); |
460 } | 477 } |
461 | 478 |
479 ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module); | |
480 | |
481 if (ctx == NULL) { | |
482 return ngx_http_next_body_filter(r, in); | |
483 } | |
484 | |
485 buf = in->buf; | |
486 | |
487 if (ngx_buf_special(in->buf)) { | |
488 return ngx_http_next_body_filter(r, in); | |
489 } | |
490 | |
491 if (ctx->offset) { | |
492 goto overlapped; | |
493 } | |
494 | |
495 range = ctx->ranges.elts; | |
496 | |
497 if (!buf->last_buf) { | |
498 | |
499 if (buf->in_file) { | |
500 start = buf->file_pos + ctx->offset; | |
501 last = buf->file_last + ctx->offset; | |
502 | |
503 } else { | |
504 start = buf->pos - buf->start + ctx->offset; | |
505 last = buf->last - buf->start + ctx->offset; | |
506 } | |
507 | |
508 for (i = 0; i < ctx->ranges.nelts; i++) { | |
509 if (start > range[i].start || last < range[i].end) { | |
510 goto overlapped; | |
511 } | |
512 } | |
513 } | |
514 | |
462 /* | 515 /* |
463 * the optimized version for the static files only | 516 * the optimized version for the responses |
464 * that are passed in the single file buf | 517 * that are passed in the single buffer |
465 */ | 518 */ |
466 | 519 |
467 if (in && in->buf->in_file && in->buf->last_buf) { | 520 ctx->offset = ngx_buf_size(buf); |
468 range = r->headers_out.ranges.elts; | 521 |
469 | 522 if (ctx->ranges.nelts == 1) { |
470 if (r->headers_out.ranges.nelts == 1) { | 523 |
471 in->buf->file_pos = range->start; | 524 if (buf->in_file) { |
472 in->buf->file_last = range->end; | 525 buf->file_pos = range->start; |
473 | 526 buf->file_last = range->end; |
474 return ngx_http_next_body_filter(r, in); | 527 } |
475 } | 528 |
476 | 529 if (ngx_buf_in_memory(buf)) { |
477 ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module); | 530 buf->pos = buf->start + (size_t) range->start; |
478 ll = &out; | 531 buf->last = buf->start + (size_t) range->end; |
479 | 532 } |
480 for (i = 0; i < r->headers_out.ranges.nelts; i++) { | 533 |
481 | 534 return ngx_http_next_body_filter(r, in); |
482 /* | 535 } |
483 * The boundary header of the range: | 536 |
484 * CRLF | 537 ll = &out; |
485 * "--0123456789" CRLF | 538 |
486 * "Content-Type: image/jpeg" CRLF | 539 for (i = 0; i < ctx->ranges.nelts; i++) { |
487 * "Content-Range: bytes " | 540 |
488 */ | 541 /* |
489 | 542 * The boundary header of the range: |
490 b = ngx_calloc_buf(r->pool); | 543 * CRLF |
491 if (b == NULL) { | 544 * "--0123456789" CRLF |
492 return NGX_ERROR; | 545 * "Content-Type: image/jpeg" CRLF |
493 } | 546 * "Content-Range: bytes " |
494 | 547 */ |
495 b->memory = 1; | |
496 b->pos = ctx->boundary_header.data; | |
497 b->last = ctx->boundary_header.data + ctx->boundary_header.len; | |
498 | |
499 hcl = ngx_alloc_chain_link(r->pool); | |
500 if (hcl == NULL) { | |
501 return NGX_ERROR; | |
502 } | |
503 | |
504 hcl->buf = b; | |
505 | |
506 | |
507 /* "SSSS-EEEE/TTTT" CRLF CRLF */ | |
508 | |
509 b = ngx_calloc_buf(r->pool); | |
510 if (b == NULL) { | |
511 return NGX_ERROR; | |
512 } | |
513 | |
514 b->temporary = 1; | |
515 b->pos = range[i].content_range.data; | |
516 b->last = range[i].content_range.data + range[i].content_range.len; | |
517 | |
518 rcl = ngx_alloc_chain_link(r->pool); | |
519 if (rcl == NULL) { | |
520 return NGX_ERROR; | |
521 } | |
522 | |
523 rcl->buf = b; | |
524 | |
525 | |
526 /* the range data */ | |
527 | |
528 b = ngx_calloc_buf(r->pool); | |
529 if (b == NULL) { | |
530 return NGX_ERROR; | |
531 } | |
532 | |
533 b->in_file = 1; | |
534 b->file_pos = range[i].start; | |
535 b->file_last = range[i].end; | |
536 b->file = in->buf->file; | |
537 | |
538 dcl = ngx_alloc_chain_link(r->pool); | |
539 if (dcl == NULL) { | |
540 return NGX_ERROR; | |
541 } | |
542 | |
543 dcl->buf = b; | |
544 | |
545 *ll = hcl; | |
546 hcl->next = rcl; | |
547 rcl->next = dcl; | |
548 ll = &dcl->next; | |
549 } | |
550 | |
551 /* the last boundary CRLF "--0123456789--" CRLF */ | |
552 | 548 |
553 b = ngx_calloc_buf(r->pool); | 549 b = ngx_calloc_buf(r->pool); |
554 if (b == NULL) { | 550 if (b == NULL) { |
555 return NGX_ERROR; | 551 return NGX_ERROR; |
556 } | 552 } |
557 | 553 |
558 b->temporary = 1; | 554 b->memory = 1; |
559 b->last_buf = 1; | 555 b->pos = ctx->boundary_header.data; |
560 | 556 b->last = ctx->boundary_header.data + ctx->boundary_header.len; |
561 b->pos = ngx_palloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN | |
562 + sizeof("--" CRLF) - 1); | |
563 if (b->pos == NULL) { | |
564 return NGX_ERROR; | |
565 } | |
566 | |
567 b->last = ngx_cpymem(b->pos, ctx->boundary_header.data, 4 + 10); | |
568 *b->last++ = '-'; *b->last++ = '-'; | |
569 *b->last++ = CR; *b->last++ = LF; | |
570 | 557 |
571 hcl = ngx_alloc_chain_link(r->pool); | 558 hcl = ngx_alloc_chain_link(r->pool); |
572 if (hcl == NULL) { | 559 if (hcl == NULL) { |
573 return NGX_ERROR; | 560 return NGX_ERROR; |
574 } | 561 } |
575 | 562 |
576 hcl->buf = b; | 563 hcl->buf = b; |
577 hcl->next = NULL; | 564 |
565 | |
566 /* "SSSS-EEEE/TTTT" CRLF CRLF */ | |
567 | |
568 b = ngx_calloc_buf(r->pool); | |
569 if (b == NULL) { | |
570 return NGX_ERROR; | |
571 } | |
572 | |
573 b->temporary = 1; | |
574 b->pos = range[i].content_range.data; | |
575 b->last = range[i].content_range.data + range[i].content_range.len; | |
576 | |
577 rcl = ngx_alloc_chain_link(r->pool); | |
578 if (rcl == NULL) { | |
579 return NGX_ERROR; | |
580 } | |
581 | |
582 rcl->buf = b; | |
583 | |
584 | |
585 /* the range data */ | |
586 | |
587 b = ngx_calloc_buf(r->pool); | |
588 if (b == NULL) { | |
589 return NGX_ERROR; | |
590 } | |
591 | |
592 b->in_file = buf->in_file; | |
593 b->temporary = buf->temporary; | |
594 b->memory = buf->memory; | |
595 b->mmap = buf->mmap; | |
596 b->file = buf->file; | |
597 | |
598 if (buf->in_file) { | |
599 buf->file_pos = range[i].start; | |
600 buf->file_last = range[i].end; | |
601 } | |
602 | |
603 if (ngx_buf_in_memory(buf)) { | |
604 buf->pos = buf->start + (size_t) range[i].start; | |
605 buf->last = buf->start + (size_t) range[i].end; | |
606 } | |
607 | |
608 dcl = ngx_alloc_chain_link(r->pool); | |
609 if (dcl == NULL) { | |
610 return NGX_ERROR; | |
611 } | |
612 | |
613 dcl->buf = b; | |
578 | 614 |
579 *ll = hcl; | 615 *ll = hcl; |
580 | 616 hcl->next = rcl; |
581 return ngx_http_next_body_filter(r, out); | 617 rcl->next = dcl; |
582 } | 618 ll = &dcl->next; |
583 | 619 } |
584 /* TODO: alert */ | 620 |
585 | 621 /* the last boundary CRLF "--0123456789--" CRLF */ |
586 return ngx_http_next_body_filter(r, in); | 622 |
623 b = ngx_calloc_buf(r->pool); | |
624 if (b == NULL) { | |
625 return NGX_ERROR; | |
626 } | |
627 | |
628 b->temporary = 1; | |
629 b->last_buf = 1; | |
630 | |
631 b->pos = ngx_palloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN | |
632 + sizeof("--" CRLF) - 1); | |
633 if (b->pos == NULL) { | |
634 return NGX_ERROR; | |
635 } | |
636 | |
637 b->last = ngx_cpymem(b->pos, ctx->boundary_header.data, 4 + 10); | |
638 *b->last++ = '-'; *b->last++ = '-'; | |
639 *b->last++ = CR; *b->last++ = LF; | |
640 | |
641 hcl = ngx_alloc_chain_link(r->pool); | |
642 if (hcl == NULL) { | |
643 return NGX_ERROR; | |
644 } | |
645 | |
646 hcl->buf = b; | |
647 hcl->next = NULL; | |
648 | |
649 *ll = hcl; | |
650 | |
651 return ngx_http_next_body_filter(r, out); | |
652 | |
653 overlapped: | |
654 | |
655 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
656 "range in overlapped buffers"); | |
657 | |
658 return NGX_ERROR; | |
587 } | 659 } |
588 | 660 |
589 | 661 |
590 static ngx_int_t | 662 static ngx_int_t |
591 ngx_http_range_header_filter_init(ngx_conf_t *cf) | 663 ngx_http_range_header_filter_init(ngx_conf_t *cf) |