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)