comparison src/http/modules/ngx_http_range_filter_module.c @ 2118:c1f589732d8c

split ngx_http_range_filter_module
author Igor Sysoev <igor@sysoev.ru>
date Wed, 23 Jul 2008 16:18:37 +0000
parents 2a92804f4109
children 0799703985ef
comparison
equal deleted inserted replaced
2117:9d62abd76ea5 2118:c1f589732d8c
56 ngx_str_t boundary_header; 56 ngx_str_t boundary_header;
57 ngx_array_t ranges; 57 ngx_array_t ranges;
58 } ngx_http_range_filter_ctx_t; 58 } ngx_http_range_filter_ctx_t;
59 59
60 60
61 ngx_int_t ngx_http_range_parse(ngx_http_request_t *r,
62 ngx_http_range_filter_ctx_t *ctx);
63 static ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r,
64 ngx_http_range_filter_ctx_t *ctx);
65 static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r,
66 ngx_http_range_filter_ctx_t *ctx);
67 static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r,
68 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
69 static ngx_int_t ngx_http_range_singlepart_body(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_multipart_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_not_satisfiable(ngx_http_request_t *r);
74
61 static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf); 75 static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf);
62 static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf); 76 static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf);
63 77
64 78
65 static ngx_http_module_t ngx_http_range_header_filter_module_ctx = { 79 static ngx_http_module_t ngx_http_range_header_filter_module_ctx = {
129 143
130 144
131 static ngx_int_t 145 static ngx_int_t
132 ngx_http_range_header_filter(ngx_http_request_t *r) 146 ngx_http_range_header_filter(ngx_http_request_t *r)
133 { 147 {
134 u_char *p;
135 size_t len;
136 off_t start, end;
137 time_t if_range; 148 time_t if_range;
138 ngx_int_t rc; 149 ngx_int_t rc;
139 ngx_uint_t suffix, i;
140 ngx_atomic_uint_t boundary;
141 ngx_table_elt_t *content_range;
142 ngx_http_range_t *range;
143 ngx_http_range_filter_ctx_t *ctx; 150 ngx_http_range_filter_ctx_t *ctx;
144 151
145 if (r->http_version < NGX_HTTP_VERSION_10 152 if (r->http_version < NGX_HTTP_VERSION_10
146 || r->headers_out.status != NGX_HTTP_OK 153 || r->headers_out.status != NGX_HTTP_OK
147 || r != r->main 154 || r != r->main
183 == NGX_ERROR) 190 == NGX_ERROR)
184 { 191 {
185 return NGX_ERROR; 192 return NGX_ERROR;
186 } 193 }
187 194
188 rc = 0; 195 rc = ngx_http_range_parse(r, ctx);
189 range = NULL; 196
197 if (rc == NGX_OK) {
198
199 ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
200
201 r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
202
203 if (ctx->ranges.nelts == 1) {
204 return ngx_http_range_singlepart_header(r, ctx);
205 }
206
207 return ngx_http_range_multipart_header(r, ctx);
208 }
209
210 if (rc == NGX_HTTP_RANGE_NOT_SATISFIABLE) {
211 return ngx_http_range_not_satisfiable(r);
212 }
213
214 /* rc == NGX_ERROR */
215
216 return rc;
217
218 next_filter:
219
220 r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
221 if (r->headers_out.accept_ranges == NULL) {
222 return NGX_ERROR;
223 }
224
225 r->headers_out.accept_ranges->hash = 1;
226 r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1;
227 r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges";
228 r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1;
229 r->headers_out.accept_ranges->value.data = (u_char *) "bytes";
230
231 return ngx_http_next_header_filter(r);
232 }
233
234
235 ngx_int_t
236 ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx)
237 {
238 u_char *p;
239 off_t start, end;
240 ngx_uint_t suffix;
241 ngx_http_range_t *range;
242
190 p = r->headers_in.range->value.data + 6; 243 p = r->headers_in.range->value.data + 6;
191 244
192 for ( ;; ) { 245 for ( ;; ) {
193 start = 0; 246 start = 0;
194 end = 0; 247 end = 0;
196 249
197 while (*p == ' ') { p++; } 250 while (*p == ' ') { p++; }
198 251
199 if (*p != '-') { 252 if (*p != '-') {
200 if (*p < '0' || *p > '9') { 253 if (*p < '0' || *p > '9') {
201 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; 254 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
202 break;
203 } 255 }
204 256
205 while (*p >= '0' && *p <= '9') { 257 while (*p >= '0' && *p <= '9') {
206 start = start * 10 + *p++ - '0'; 258 start = start * 10 + *p++ - '0';
207 } 259 }
208 260
209 while (*p == ' ') { p++; } 261 while (*p == ' ') { p++; }
210 262
211 if (*p++ != '-') { 263 if (*p++ != '-') {
212 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; 264 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
213 break;
214 } 265 }
215 266
216 if (start >= r->headers_out.content_length_n) { 267 if (start >= r->headers_out.content_length_n) {
217 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; 268 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
218 break;
219 } 269 }
220 270
221 while (*p == ' ') { p++; } 271 while (*p == ' ') { p++; }
222 272
223 if (*p == ',' || *p == '\0') { 273 if (*p == ',' || *p == '\0') {
228 278
229 range->start = start; 279 range->start = start;
230 range->end = r->headers_out.content_length_n; 280 range->end = r->headers_out.content_length_n;
231 281
232 if (*p++ != ',') { 282 if (*p++ != ',') {
233 break; 283 return NGX_OK;
234 } 284 }
235 285
236 continue; 286 continue;
237 } 287 }
238 288
240 suffix = 1; 290 suffix = 1;
241 p++; 291 p++;
242 } 292 }
243 293
244 if (*p < '0' || *p > '9') { 294 if (*p < '0' || *p > '9') {
245 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; 295 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
246 break;
247 } 296 }
248 297
249 while (*p >= '0' && *p <= '9') { 298 while (*p >= '0' && *p <= '9') {
250 end = end * 10 + *p++ - '0'; 299 end = end * 10 + *p++ - '0';
251 } 300 }
252 301
253 while (*p == ' ') { p++; } 302 while (*p == ' ') { p++; }
254 303
255 if (*p != ',' && *p != '\0') { 304 if (*p != ',' && *p != '\0') {
256 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; 305 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
257 break;
258 } 306 }
259 307
260 if (suffix) { 308 if (suffix) {
261 start = r->headers_out.content_length_n - end; 309 start = r->headers_out.content_length_n - end;
262 end = r->headers_out.content_length_n - 1; 310 end = r->headers_out.content_length_n - 1;
263 } 311 }
264 312
265 if (start > end) { 313 if (start > end) {
266 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; 314 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
267 break;
268 } 315 }
269 316
270 range = ngx_array_push(&ctx->ranges); 317 range = ngx_array_push(&ctx->ranges);
271 if (range == NULL) { 318 if (range == NULL) {
272 return NGX_ERROR; 319 return NGX_ERROR;
284 } else { 331 } else {
285 range->end = end + 1; 332 range->end = end + 1;
286 } 333 }
287 334
288 if (*p++ != ',') { 335 if (*p++ != ',') {
289 break; 336 return NGX_OK;
290 } 337 }
291 } 338 }
292 339 }
293 if (rc) { 340
294 341
295 /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */ 342 static ngx_int_t
296 343 ngx_http_range_singlepart_header(ngx_http_request_t *r,
297 r->headers_out.status = rc; 344 ngx_http_range_filter_ctx_t *ctx)
298 345 {
299 content_range = ngx_list_push(&r->headers_out.headers); 346 ngx_table_elt_t *content_range;
300 if (content_range == NULL) { 347 ngx_http_range_t *range;
301 return NGX_ERROR; 348
302 } 349 content_range = ngx_list_push(&r->headers_out.headers);
303 350 if (content_range == NULL) {
304 r->headers_out.content_range = content_range; 351 return NGX_ERROR;
305 352 }
306 content_range->hash = 1; 353
307 content_range->key.len = sizeof("Content-Range") - 1; 354 r->headers_out.content_range = content_range;
308 content_range->key.data = (u_char *) "Content-Range"; 355
309 356 content_range->hash = 1;
310 content_range->value.data = ngx_pnalloc(r->pool, 357 content_range->key.len = sizeof("Content-Range") - 1;
311 sizeof("bytes */") - 1 + NGX_OFF_T_LEN); 358 content_range->key.data = (u_char *) "Content-Range";
312 if (content_range->value.data == NULL) { 359
313 return NGX_ERROR; 360 content_range->value.data = ngx_pnalloc(r->pool,
314 } 361 sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
315 362 if (content_range->value.data == NULL) {
316 content_range->value.len = ngx_sprintf(content_range->value.data, 363 return NGX_ERROR;
317 "bytes */%O", 364 }
318 r->headers_out.content_length_n) 365
319 - content_range->value.data; 366 /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
320 367
321 ngx_http_clear_content_length(r); 368 range = ctx->ranges.elts;
322 369
323 return rc; 370 content_range->value.len = ngx_sprintf(content_range->value.data,
324 } 371 "bytes %O-%O/%O",
325 372 range->start, range->end - 1,
326 ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); 373 r->headers_out.content_length_n)
327 374 - content_range->value.data;
328 r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; 375
329 376 r->headers_out.content_length_n = range->end - range->start;
330 if (ctx->ranges.nelts == 1) { 377
331 378 if (r->headers_out.content_length) {
332 content_range = ngx_list_push(&r->headers_out.headers); 379 r->headers_out.content_length->hash = 0;
333 if (content_range == NULL) { 380 r->headers_out.content_length = NULL;
334 return NGX_ERROR; 381 }
335 } 382
336 383 return ngx_http_next_header_filter(r);
337 r->headers_out.content_range = content_range; 384 }
338 385
339 content_range->hash = 1; 386
340 content_range->key.len = sizeof("Content-Range") - 1; 387 static ngx_int_t
341 content_range->key.data = (u_char *) "Content-Range"; 388 ngx_http_range_multipart_header(ngx_http_request_t *r,
342 389 ngx_http_range_filter_ctx_t *ctx)
343 content_range->value.data = 390 {
344 ngx_pnalloc(r->pool, sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN); 391 size_t len;
345 if (content_range->value.data == NULL) { 392 ngx_uint_t i;
346 return NGX_ERROR; 393 ngx_http_range_t *range;
347 } 394 ngx_atomic_uint_t boundary;
348
349 /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
350
351 content_range->value.len = ngx_sprintf(content_range->value.data,
352 "bytes %O-%O/%O",
353 range->start, range->end - 1,
354 r->headers_out.content_length_n)
355 - content_range->value.data;
356
357 r->headers_out.content_length_n = range->end - range->start;
358
359 if (r->headers_out.content_length) {
360 r->headers_out.content_length->hash = 0;
361 r->headers_out.content_length = NULL;
362 }
363
364 return ngx_http_next_header_filter(r);
365 }
366
367 395
368 /* TODO: what if no content_type ?? */ 396 /* TODO: what if no content_type ?? */
369 397
370 len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN 398 len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
371 + sizeof(CRLF "Content-Type: ") - 1 399 + sizeof(CRLF "Content-Type: ") - 1
463 r->headers_out.content_length->hash = 0; 491 r->headers_out.content_length->hash = 0;
464 r->headers_out.content_length = NULL; 492 r->headers_out.content_length = NULL;
465 } 493 }
466 494
467 return ngx_http_next_header_filter(r); 495 return ngx_http_next_header_filter(r);
468 496 }
469 next_filter: 497
470 498
471 r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers); 499 static ngx_int_t
472 if (r->headers_out.accept_ranges == NULL) { 500 ngx_http_range_not_satisfiable(ngx_http_request_t *r)
473 return NGX_ERROR; 501 {
474 } 502 ngx_table_elt_t *content_range;
475 503
476 r->headers_out.accept_ranges->hash = 1; 504 r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
477 r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1; 505
478 r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges"; 506 content_range = ngx_list_push(&r->headers_out.headers);
479 r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1; 507 if (content_range == NULL) {
480 r->headers_out.accept_ranges->value.data = (u_char *) "bytes"; 508 return NGX_ERROR;
481 509 }
482 return ngx_http_next_header_filter(r); 510
511 r->headers_out.content_range = content_range;
512
513 content_range->hash = 1;
514 content_range->key.len = sizeof("Content-Range") - 1;
515 content_range->key.data = (u_char *) "Content-Range";
516
517 content_range->value.data = ngx_pnalloc(r->pool,
518 sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
519 if (content_range->value.data == NULL) {
520 return NGX_ERROR;
521 }
522
523 content_range->value.len = ngx_sprintf(content_range->value.data,
524 "bytes */%O",
525 r->headers_out.content_length_n)
526 - content_range->value.data;
527
528 ngx_http_clear_content_length(r);
529
530 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
483 } 531 }
484 532
485 533
486 static ngx_int_t 534 static ngx_int_t
487 ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in) 535 ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
488 { 536 {
489 off_t start, last;
490 ngx_buf_t *b, *buf;
491 ngx_uint_t i;
492 ngx_chain_t *out, *hcl, *rcl, *dcl, **ll;
493 ngx_http_range_t *range;
494 ngx_http_range_filter_ctx_t *ctx; 537 ngx_http_range_filter_ctx_t *ctx;
495 538
496 if (in == NULL) { 539 if (in == NULL) {
497 return ngx_http_next_body_filter(r, in); 540 return ngx_http_next_body_filter(r, in);
498 } 541 }
501 544
502 if (ctx == NULL) { 545 if (ctx == NULL) {
503 return ngx_http_next_body_filter(r, in); 546 return ngx_http_next_body_filter(r, in);
504 } 547 }
505 548
506 buf = in->buf;
507
508 if (ngx_buf_special(in->buf)) { 549 if (ngx_buf_special(in->buf)) {
509 return ngx_http_next_body_filter(r, in); 550 return ngx_http_next_body_filter(r, in);
510 } 551 }
511 552
553 if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {
554 return NGX_ERROR;
555 }
556
557 if (ctx->ranges.nelts == 1) {
558
559 /*
560 * the optimized version for the responses
561 * that are passed in the single buffer
562 */
563
564 return ngx_http_range_singlepart_body(r, ctx, in);
565 }
566
567 return ngx_http_range_multipart_body(r, ctx, in);
568 }
569
570
571 static ngx_int_t
572 ngx_http_range_test_overlapped(ngx_http_request_t *r,
573 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
574 {
575 off_t start, last;
576 ngx_buf_t *buf;
577 ngx_uint_t i;
578 ngx_http_range_t *range;
579
512 if (ctx->offset) { 580 if (ctx->offset) {
513 goto overlapped; 581 goto overlapped;
514 } 582 }
515 583
516 range = ctx->ranges.elts; 584 buf = in->buf;
517 585
518 if (!buf->last_buf) { 586 if (!buf->last_buf) {
519 587
520 if (buf->in_file) { 588 if (buf->in_file) {
521 start = buf->file_pos + ctx->offset; 589 start = buf->file_pos + ctx->offset;
524 } else { 592 } else {
525 start = buf->pos - buf->start + ctx->offset; 593 start = buf->pos - buf->start + ctx->offset;
526 last = buf->last - buf->start + ctx->offset; 594 last = buf->last - buf->start + ctx->offset;
527 } 595 }
528 596
597 range = ctx->ranges.elts;
529 for (i = 0; i < ctx->ranges.nelts; i++) { 598 for (i = 0; i < ctx->ranges.nelts; i++) {
530 if (start > range[i].start || last < range[i].end) { 599 if (start > range[i].start || last < range[i].end) {
531 goto overlapped; 600 goto overlapped;
532 } 601 }
533 } 602 }
534 } 603 }
535 604
536 /*
537 * the optimized version for the responses
538 * that are passed in the single buffer
539 */
540
541 ctx->offset = ngx_buf_size(buf); 605 ctx->offset = ngx_buf_size(buf);
542 606
543 if (ctx->ranges.nelts == 1) { 607 return NGX_OK;
544 608
545 if (buf->in_file) { 609 overlapped:
546 buf->file_pos = range->start; 610
547 buf->file_last = range->end; 611 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
548 } 612 "range in overlapped buffers");
549 613
550 if (ngx_buf_in_memory(buf)) { 614 return NGX_ERROR;
551 buf->pos = buf->start + (size_t) range->start; 615 }
552 buf->last = buf->start + (size_t) range->end; 616
553 } 617
554 618 static ngx_int_t
555 return ngx_http_next_body_filter(r, in); 619 ngx_http_range_singlepart_body(ngx_http_request_t *r,
556 } 620 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
621 {
622 ngx_buf_t *buf;
623 ngx_http_range_t *range;
624
625 buf = in->buf;
626 range = ctx->ranges.elts;
627
628 if (buf->in_file) {
629 buf->file_pos = range->start;
630 buf->file_last = range->end;
631 }
632
633 if (ngx_buf_in_memory(buf)) {
634 buf->pos = buf->start + (size_t) range->start;
635 buf->last = buf->start + (size_t) range->end;
636 }
637
638 return ngx_http_next_body_filter(r, in);
639 }
640
641
642 static ngx_int_t
643 ngx_http_range_multipart_body(ngx_http_request_t *r,
644 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
645 {
646 ngx_buf_t *b, *buf;
647 ngx_uint_t i;
648 ngx_chain_t *out, *hcl, *rcl, *dcl, **ll;
649 ngx_http_range_t *range;
557 650
558 ll = &out; 651 ll = &out;
652 buf = in->buf;
653 range = ctx->ranges.elts;
559 654
560 for (i = 0; i < ctx->ranges.nelts; i++) { 655 for (i = 0; i < ctx->ranges.nelts; i++) {
561 656
562 /* 657 /*
563 * The boundary header of the range: 658 * The boundary header of the range:
669 hcl->next = NULL; 764 hcl->next = NULL;
670 765
671 *ll = hcl; 766 *ll = hcl;
672 767
673 return ngx_http_next_body_filter(r, out); 768 return ngx_http_next_body_filter(r, out);
674
675 overlapped:
676
677 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
678 "range in overlapped buffers");
679
680 return NGX_ERROR;
681 } 769 }
682 770
683 771
684 static ngx_int_t 772 static ngx_int_t
685 ngx_http_range_header_filter_init(ngx_conf_t *cf) 773 ngx_http_range_header_filter_init(ngx_conf_t *cf)