Mercurial > hg > nginx-ranges
comparison src/http/modules/ngx_http_range_filter_module.c @ 396:77df96611112
Merge with current.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Tue, 05 Aug 2008 02:12:51 +0400 |
parents | 1d9bef53cd8e 0b6053502c55 |
children | 44a61c599bb2 |
comparison
equal
deleted
inserted
replaced
391:1d9bef53cd8e | 396:77df96611112 |
---|---|
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_not_satisfiable(ngx_http_request_t *r); | |
68 static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r, | |
69 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in); | |
70 static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r, | |
71 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in, | |
72 ngx_http_output_body_filter_pt ngx_http_next_body_filter); | |
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 ngx_http_output_body_filter_pt ngx_http_next_body_filter); | |
76 | |
61 static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf); | 77 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); | 78 static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf); |
63 static ngx_int_t ngx_http_range_late_filter_init(ngx_conf_t *cf); | 79 static ngx_int_t ngx_http_range_late_filter_init(ngx_conf_t *cf); |
64 | 80 |
65 | 81 |
162 | 178 |
163 | 179 |
164 static ngx_int_t | 180 static ngx_int_t |
165 ngx_http_range_header_filter(ngx_http_request_t *r) | 181 ngx_http_range_header_filter(ngx_http_request_t *r) |
166 { | 182 { |
167 u_char *p; | |
168 size_t len; | |
169 off_t start, end; | |
170 time_t if_range; | 183 time_t if_range; |
171 ngx_int_t rc; | 184 ngx_int_t rc; |
172 ngx_uint_t suffix, i; | |
173 ngx_atomic_uint_t boundary; | |
174 ngx_table_elt_t *content_range; | |
175 ngx_http_range_t *range; | |
176 ngx_http_range_filter_ctx_t *ctx; | 185 ngx_http_range_filter_ctx_t *ctx; |
177 | 186 |
178 if (r->http_version < NGX_HTTP_VERSION_10 | 187 if (r->http_version < NGX_HTTP_VERSION_10 |
179 || r->headers_out.status != NGX_HTTP_OK | 188 || r->headers_out.status != NGX_HTTP_OK |
180 || r != r->main | 189 || r != r->main |
216 == NGX_ERROR) | 225 == NGX_ERROR) |
217 { | 226 { |
218 return NGX_ERROR; | 227 return NGX_ERROR; |
219 } | 228 } |
220 | 229 |
221 rc = 0; | 230 rc = ngx_http_range_parse(r, ctx); |
222 range = NULL; | 231 |
232 if (rc == NGX_OK) { | |
233 | |
234 ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); | |
235 | |
236 r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; | |
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 | |
245 if (rc == NGX_HTTP_RANGE_NOT_SATISFIABLE) { | |
246 return ngx_http_range_not_satisfiable(r); | |
247 } | |
248 | |
249 /* rc == NGX_ERROR */ | |
250 | |
251 return rc; | |
252 | |
253 next_filter: | |
254 | |
255 r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers); | |
256 if (r->headers_out.accept_ranges == NULL) { | |
257 return NGX_ERROR; | |
258 } | |
259 | |
260 r->headers_out.accept_ranges->hash = 1; | |
261 r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1; | |
262 r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges"; | |
263 r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1; | |
264 r->headers_out.accept_ranges->value.data = (u_char *) "bytes"; | |
265 | |
266 return ngx_http_next_header_filter(r); | |
267 } | |
268 | |
269 | |
270 ngx_int_t | |
271 ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx) | |
272 { | |
273 u_char *p; | |
274 off_t start, end; | |
275 ngx_uint_t suffix; | |
276 ngx_http_range_t *range; | |
277 | |
223 p = r->headers_in.range->value.data + 6; | 278 p = r->headers_in.range->value.data + 6; |
224 | 279 |
225 for ( ;; ) { | 280 for ( ;; ) { |
226 start = 0; | 281 start = 0; |
227 end = 0; | 282 end = 0; |
229 | 284 |
230 while (*p == ' ') { p++; } | 285 while (*p == ' ') { p++; } |
231 | 286 |
232 if (*p != '-') { | 287 if (*p != '-') { |
233 if (*p < '0' || *p > '9') { | 288 if (*p < '0' || *p > '9') { |
234 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | 289 return NGX_HTTP_RANGE_NOT_SATISFIABLE; |
235 break; | |
236 } | 290 } |
237 | 291 |
238 while (*p >= '0' && *p <= '9') { | 292 while (*p >= '0' && *p <= '9') { |
239 start = start * 10 + *p++ - '0'; | 293 start = start * 10 + *p++ - '0'; |
240 } | 294 } |
241 | 295 |
242 while (*p == ' ') { p++; } | 296 while (*p == ' ') { p++; } |
243 | 297 |
244 if (*p++ != '-') { | 298 if (*p++ != '-') { |
245 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | 299 return NGX_HTTP_RANGE_NOT_SATISFIABLE; |
246 break; | |
247 } | 300 } |
248 | 301 |
249 if (start >= r->headers_out.content_length_n) { | 302 if (start >= r->headers_out.content_length_n) { |
250 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | 303 return NGX_HTTP_RANGE_NOT_SATISFIABLE; |
251 break; | |
252 } | 304 } |
253 | 305 |
254 while (*p == ' ') { p++; } | 306 while (*p == ' ') { p++; } |
255 | 307 |
256 if (*p == ',' || *p == '\0') { | 308 if (*p == ',' || *p == '\0') { |
261 | 313 |
262 range->start = start; | 314 range->start = start; |
263 range->end = r->headers_out.content_length_n; | 315 range->end = r->headers_out.content_length_n; |
264 | 316 |
265 if (*p++ != ',') { | 317 if (*p++ != ',') { |
266 break; | 318 return NGX_OK; |
267 } | 319 } |
268 | 320 |
269 continue; | 321 continue; |
270 } | 322 } |
271 | 323 |
273 suffix = 1; | 325 suffix = 1; |
274 p++; | 326 p++; |
275 } | 327 } |
276 | 328 |
277 if (*p < '0' || *p > '9') { | 329 if (*p < '0' || *p > '9') { |
278 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | 330 return NGX_HTTP_RANGE_NOT_SATISFIABLE; |
279 break; | |
280 } | 331 } |
281 | 332 |
282 while (*p >= '0' && *p <= '9') { | 333 while (*p >= '0' && *p <= '9') { |
283 end = end * 10 + *p++ - '0'; | 334 end = end * 10 + *p++ - '0'; |
284 } | 335 } |
285 | 336 |
286 while (*p == ' ') { p++; } | 337 while (*p == ' ') { p++; } |
287 | 338 |
288 if (*p != ',' && *p != '\0') { | 339 if (*p != ',' && *p != '\0') { |
289 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | 340 return NGX_HTTP_RANGE_NOT_SATISFIABLE; |
290 break; | |
291 } | 341 } |
292 | 342 |
293 if (suffix) { | 343 if (suffix) { |
294 start = r->headers_out.content_length_n - end; | 344 start = r->headers_out.content_length_n - end; |
295 end = r->headers_out.content_length_n - 1; | 345 end = r->headers_out.content_length_n - 1; |
296 } | 346 } |
297 | 347 |
298 if (start > end) { | 348 if (start > end) { |
299 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | 349 return NGX_HTTP_RANGE_NOT_SATISFIABLE; |
300 break; | |
301 } | 350 } |
302 | 351 |
303 range = ngx_array_push(&ctx->ranges); | 352 range = ngx_array_push(&ctx->ranges); |
304 if (range == NULL) { | 353 if (range == NULL) { |
305 return NGX_ERROR; | 354 return NGX_ERROR; |
317 } else { | 366 } else { |
318 range->end = end + 1; | 367 range->end = end + 1; |
319 } | 368 } |
320 | 369 |
321 if (*p++ != ',') { | 370 if (*p++ != ',') { |
322 break; | 371 return NGX_OK; |
323 } | 372 } |
324 } | 373 } |
325 | 374 } |
326 if (rc) { | 375 |
327 | 376 |
328 /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */ | 377 static ngx_int_t |
329 | 378 ngx_http_range_singlepart_header(ngx_http_request_t *r, |
330 r->headers_out.status = rc; | 379 ngx_http_range_filter_ctx_t *ctx) |
331 | 380 { |
332 content_range = ngx_list_push(&r->headers_out.headers); | 381 ngx_table_elt_t *content_range; |
333 if (content_range == NULL) { | 382 ngx_http_range_t *range; |
334 return NGX_ERROR; | 383 |
335 } | 384 content_range = ngx_list_push(&r->headers_out.headers); |
336 | 385 if (content_range == NULL) { |
337 r->headers_out.content_range = content_range; | 386 return NGX_ERROR; |
338 | 387 } |
339 content_range->hash = 1; | 388 |
340 content_range->key.len = sizeof("Content-Range") - 1; | 389 r->headers_out.content_range = content_range; |
341 content_range->key.data = (u_char *) "Content-Range"; | 390 |
342 | 391 content_range->hash = 1; |
343 content_range->value.data = ngx_pnalloc(r->pool, | 392 content_range->key.len = sizeof("Content-Range") - 1; |
344 sizeof("bytes */") - 1 + NGX_OFF_T_LEN); | 393 content_range->key.data = (u_char *) "Content-Range"; |
345 if (content_range->value.data == NULL) { | 394 |
346 return NGX_ERROR; | 395 content_range->value.data = ngx_pnalloc(r->pool, |
347 } | 396 sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN); |
348 | 397 if (content_range->value.data == NULL) { |
349 content_range->value.len = ngx_sprintf(content_range->value.data, | 398 return NGX_ERROR; |
350 "bytes */%O", | 399 } |
351 r->headers_out.content_length_n) | 400 |
352 - content_range->value.data; | 401 /* "Content-Range: bytes SSSS-EEEE/TTTT" header */ |
353 | 402 |
354 ngx_http_clear_content_length(r); | 403 range = ctx->ranges.elts; |
355 | 404 |
356 return rc; | 405 content_range->value.len = ngx_sprintf(content_range->value.data, |
357 } | 406 "bytes %O-%O/%O", |
358 | 407 range->start, range->end - 1, |
359 ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); | 408 r->headers_out.content_length_n) |
360 | 409 - content_range->value.data; |
361 r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; | 410 |
362 | 411 r->headers_out.content_length_n = range->end - range->start; |
363 if (ctx->ranges.nelts == 1) { | 412 |
364 | 413 if (r->headers_out.content_length) { |
365 content_range = ngx_list_push(&r->headers_out.headers); | 414 r->headers_out.content_length->hash = 0; |
366 if (content_range == NULL) { | 415 r->headers_out.content_length = NULL; |
367 return NGX_ERROR; | 416 } |
368 } | 417 |
369 | 418 return ngx_http_next_header_filter(r); |
370 r->headers_out.content_range = content_range; | 419 } |
371 | 420 |
372 content_range->hash = 1; | 421 |
373 content_range->key.len = sizeof("Content-Range") - 1; | 422 static ngx_int_t |
374 content_range->key.data = (u_char *) "Content-Range"; | 423 ngx_http_range_multipart_header(ngx_http_request_t *r, |
375 | 424 ngx_http_range_filter_ctx_t *ctx) |
376 content_range->value.data = | 425 { |
377 ngx_pnalloc(r->pool, sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN); | 426 size_t len; |
378 if (content_range->value.data == NULL) { | 427 ngx_uint_t i; |
379 return NGX_ERROR; | 428 ngx_http_range_t *range; |
380 } | 429 ngx_atomic_uint_t boundary; |
381 | |
382 /* "Content-Range: bytes SSSS-EEEE/TTTT" header */ | |
383 | |
384 content_range->value.len = ngx_sprintf(content_range->value.data, | |
385 "bytes %O-%O/%O", | |
386 range->start, range->end - 1, | |
387 r->headers_out.content_length_n) | |
388 - content_range->value.data; | |
389 | |
390 r->headers_out.content_length_n = range->end - range->start; | |
391 | |
392 if (r->headers_out.content_length) { | |
393 r->headers_out.content_length->hash = 0; | |
394 r->headers_out.content_length = NULL; | |
395 } | |
396 | |
397 return ngx_http_next_header_filter(r); | |
398 } | |
399 | |
400 | |
401 /* TODO: what if no content_type ?? */ | |
402 | 430 |
403 len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN | 431 len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN |
404 + sizeof(CRLF "Content-Type: ") - 1 | 432 + sizeof(CRLF "Content-Type: ") - 1 |
405 + r->headers_out.content_type.len | 433 + r->headers_out.content_type.len |
406 + sizeof(CRLF "Content-Range: bytes ") - 1; | 434 + sizeof(CRLF "Content-Range: bytes ") - 1; |
434 &r->headers_out.charset) | 462 &r->headers_out.charset) |
435 - ctx->boundary_header.data; | 463 - ctx->boundary_header.data; |
436 | 464 |
437 r->headers_out.charset.len = 0; | 465 r->headers_out.charset.len = 0; |
438 | 466 |
439 } else { | 467 } else if (r->headers_out.content_type.len) { |
440 ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data, | 468 ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data, |
441 CRLF "--%0muA" CRLF | 469 CRLF "--%0muA" CRLF |
442 "Content-Type: %V" CRLF | 470 "Content-Type: %V" CRLF |
443 "Content-Range: bytes ", | 471 "Content-Range: bytes ", |
444 boundary, | 472 boundary, |
445 &r->headers_out.content_type) | 473 &r->headers_out.content_type) |
446 - ctx->boundary_header.data; | 474 - ctx->boundary_header.data; |
475 | |
476 } else { | |
477 ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data, | |
478 CRLF "--%0muA" CRLF | |
479 "Content-Range: bytes ", | |
480 boundary) | |
481 - ctx->boundary_header.data; | |
447 } | 482 } |
448 | 483 |
449 r->headers_out.content_type.data = | 484 r->headers_out.content_type.data = |
450 ngx_pnalloc(r->pool, | 485 ngx_pnalloc(r->pool, |
451 sizeof("Content-Type: multipart/byteranges; boundary=") - 1 | 486 sizeof("Content-Type: multipart/byteranges; boundary=") - 1 |
496 r->headers_out.content_length->hash = 0; | 531 r->headers_out.content_length->hash = 0; |
497 r->headers_out.content_length = NULL; | 532 r->headers_out.content_length = NULL; |
498 } | 533 } |
499 | 534 |
500 return ngx_http_next_header_filter(r); | 535 return ngx_http_next_header_filter(r); |
501 | 536 } |
502 next_filter: | 537 |
503 | 538 |
504 r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers); | 539 static ngx_int_t |
505 if (r->headers_out.accept_ranges == NULL) { | 540 ngx_http_range_not_satisfiable(ngx_http_request_t *r) |
506 return NGX_ERROR; | 541 { |
507 } | 542 ngx_table_elt_t *content_range; |
508 | 543 |
509 r->headers_out.accept_ranges->hash = 1; | 544 r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE; |
510 r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1; | 545 |
511 r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges"; | 546 content_range = ngx_list_push(&r->headers_out.headers); |
512 r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1; | 547 if (content_range == NULL) { |
513 r->headers_out.accept_ranges->value.data = (u_char *) "bytes"; | 548 return NGX_ERROR; |
514 | 549 } |
515 return ngx_http_next_header_filter(r); | 550 |
551 r->headers_out.content_range = content_range; | |
552 | |
553 content_range->hash = 1; | |
554 content_range->key.len = sizeof("Content-Range") - 1; | |
555 content_range->key.data = (u_char *) "Content-Range"; | |
556 | |
557 content_range->value.data = ngx_pnalloc(r->pool, | |
558 sizeof("bytes */") - 1 + NGX_OFF_T_LEN); | |
559 if (content_range->value.data == NULL) { | |
560 return NGX_ERROR; | |
561 } | |
562 | |
563 content_range->value.len = ngx_sprintf(content_range->value.data, | |
564 "bytes */%O", | |
565 r->headers_out.content_length_n) | |
566 - content_range->value.data; | |
567 | |
568 ngx_http_clear_content_length(r); | |
569 | |
570 return NGX_HTTP_RANGE_NOT_SATISFIABLE; | |
516 } | 571 } |
517 | 572 |
518 | 573 |
519 static ngx_int_t | 574 static ngx_int_t |
520 ngx_http_range_body_generic_filter(ngx_http_request_t *r, ngx_chain_t *in, | 575 ngx_http_range_body_generic_filter(ngx_http_request_t *r, ngx_chain_t *in, |
521 ngx_http_output_body_filter_pt ngx_http_next_body_filter) | 576 ngx_http_output_body_filter_pt ngx_http_next_body_filter) |
522 { | 577 { |
523 off_t start, last; | |
524 ngx_buf_t *b, *buf; | |
525 ngx_uint_t i; | |
526 ngx_chain_t *out, *cl, *hcl, *rcl, *dcl, **ll; | |
527 ngx_http_range_t *range; | |
528 ngx_http_range_filter_ctx_t *ctx; | 578 ngx_http_range_filter_ctx_t *ctx; |
529 | 579 |
530 if (in == NULL) { | 580 if (in == NULL) { |
531 return ngx_http_next_body_filter(r, in); | 581 return ngx_http_next_body_filter(r, in); |
532 } | 582 } |
535 | 585 |
536 if (ctx == NULL) { | 586 if (ctx == NULL) { |
537 return ngx_http_next_body_filter(r, in); | 587 return ngx_http_next_body_filter(r, in); |
538 } | 588 } |
539 | 589 |
540 buf = in->buf; | 590 if (ctx->ranges.nelts == 1) { |
591 return ngx_http_range_singlepart_body(r, ctx, in, | |
592 ngx_http_next_body_filter); | |
593 } | |
594 | |
595 /* | |
596 * multipart ranges are supported only if whole body is in a single buffer | |
597 */ | |
541 | 598 |
542 if (ngx_buf_special(in->buf)) { | 599 if (ngx_buf_special(in->buf)) { |
543 return ngx_http_next_body_filter(r, in); | 600 return ngx_http_next_body_filter(r, in); |
544 } | 601 } |
545 | 602 |
546 range = ctx->ranges.elts; | 603 if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) { |
547 | 604 return NGX_ERROR; |
548 if (ctx->ranges.nelts > 1) { | 605 } |
549 goto multipart; | 606 |
550 } | 607 return ngx_http_range_multipart_body(r, ctx, in, |
551 | 608 ngx_http_next_body_filter); |
552 /* | 609 } |
553 * the optimized version for the responses | 610 |
554 * that are passed in the single buffer | 611 |
555 */ | 612 static ngx_int_t |
556 | 613 ngx_http_range_test_overlapped(ngx_http_request_t *r, |
557 out = NULL; | 614 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in) |
558 ll = &out; | 615 { |
559 | 616 off_t start, last; |
560 for (cl = in; cl; cl = cl->next) { | 617 ngx_buf_t *buf; |
561 | 618 ngx_uint_t i; |
562 buf = cl->buf; | 619 ngx_http_range_t *range; |
563 | |
564 start = ctx->offset; | |
565 last = ctx->offset + ngx_buf_size(buf); | |
566 | |
567 ctx->offset = last; | |
568 | |
569 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
570 "range body filter: %O-%O", start, last); | |
571 | |
572 if (ngx_buf_special(buf)) { | |
573 /* pass anyway */ | |
574 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
575 "range body filter: pass special"); | |
576 *ll = cl; | |
577 ll = &cl->next; | |
578 continue; | |
579 } | |
580 | |
581 if (range->end <= start || range->start >= last) { | |
582 /* skip buffer */ | |
583 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
584 "range body filter: skip"); | |
585 buf->pos = buf->last; | |
586 continue; | |
587 } | |
588 | |
589 if (range->start > start) { | |
590 if (buf->in_file) { | |
591 buf->file_pos += range->start - start; | |
592 } | |
593 if (ngx_buf_in_memory(buf)) { | |
594 buf->pos += (size_t) (range->start - start); | |
595 } | |
596 } | |
597 | |
598 if (range->end <= last) { | |
599 if (buf->in_file) { | |
600 buf->file_last -= last - range->end; | |
601 } | |
602 if (ngx_buf_in_memory(buf)) { | |
603 buf->last -= (size_t) (last - range->end); | |
604 } | |
605 | |
606 /* we are done */ | |
607 | |
608 buf->last_buf = 1; | |
609 *ll = cl; | |
610 cl->next = NULL; | |
611 | |
612 break; | |
613 } | |
614 | |
615 *ll = cl; | |
616 ll = &cl->next; | |
617 } | |
618 | |
619 if (out == NULL) { | |
620 return NGX_OK; | |
621 } | |
622 | |
623 return ngx_http_next_body_filter(r, out); | |
624 | |
625 multipart: | |
626 | 620 |
627 if (ctx->offset) { | 621 if (ctx->offset) { |
628 goto overlapped; | 622 goto overlapped; |
629 } | 623 } |
624 | |
625 buf = in->buf; | |
630 | 626 |
631 if (!buf->last_buf) { | 627 if (!buf->last_buf) { |
632 | 628 |
633 if (buf->in_file) { | 629 if (buf->in_file) { |
634 start = buf->file_pos + ctx->offset; | 630 start = buf->file_pos + ctx->offset; |
637 } else { | 633 } else { |
638 start = buf->pos - buf->start + ctx->offset; | 634 start = buf->pos - buf->start + ctx->offset; |
639 last = buf->last - buf->start + ctx->offset; | 635 last = buf->last - buf->start + ctx->offset; |
640 } | 636 } |
641 | 637 |
638 range = ctx->ranges.elts; | |
642 for (i = 0; i < ctx->ranges.nelts; i++) { | 639 for (i = 0; i < ctx->ranges.nelts; i++) { |
643 if (start > range[i].start || last < range[i].end) { | 640 if (start > range[i].start || last < range[i].end) { |
644 goto overlapped; | 641 goto overlapped; |
645 } | 642 } |
646 } | 643 } |
647 } | 644 } |
648 | 645 |
649 ctx->offset = ngx_buf_size(buf); | 646 ctx->offset = ngx_buf_size(buf); |
650 | 647 |
648 return NGX_OK; | |
649 | |
650 overlapped: | |
651 | |
652 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
653 "range in overlapped buffers"); | |
654 | |
655 return NGX_ERROR; | |
656 } | |
657 | |
658 | |
659 static ngx_int_t | |
660 ngx_http_range_singlepart_body(ngx_http_request_t *r, | |
661 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in, | |
662 ngx_http_output_body_filter_pt ngx_http_next_body_filter) | |
663 { | |
664 off_t start, last; | |
665 ngx_buf_t *buf; | |
666 ngx_chain_t *out, *cl, **ll; | |
667 ngx_http_range_t *range; | |
668 | |
669 out = NULL; | |
651 ll = &out; | 670 ll = &out; |
671 range = ctx->ranges.elts; | |
672 | |
673 for (cl = in; cl; cl = cl->next) { | |
674 | |
675 buf = cl->buf; | |
676 | |
677 start = ctx->offset; | |
678 last = ctx->offset + ngx_buf_size(buf); | |
679 | |
680 ctx->offset = last; | |
681 | |
682 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
683 "http range body buf: %O-%O", start, last); | |
684 | |
685 if (ngx_buf_special(buf)) { | |
686 *ll = cl; | |
687 ll = &cl->next; | |
688 continue; | |
689 } | |
690 | |
691 if (range->end <= start || range->start >= last) { | |
692 | |
693 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
694 "http range body skip"); | |
695 | |
696 buf->pos = buf->last; | |
697 continue; | |
698 } | |
699 | |
700 if (range->start > start) { | |
701 | |
702 if (buf->in_file) { | |
703 buf->file_pos += range->start - start; | |
704 } | |
705 | |
706 if (ngx_buf_in_memory(buf)) { | |
707 buf->pos += (size_t) (range->start - start); | |
708 } | |
709 } | |
710 | |
711 if (range->end <= last) { | |
712 | |
713 if (buf->in_file) { | |
714 buf->file_last -= last - range->end; | |
715 } | |
716 | |
717 if (ngx_buf_in_memory(buf)) { | |
718 buf->last -= (size_t) (last - range->end); | |
719 } | |
720 | |
721 buf->last_buf = 1; | |
722 *ll = cl; | |
723 cl->next = NULL; | |
724 | |
725 break; | |
726 } | |
727 | |
728 *ll = cl; | |
729 ll = &cl->next; | |
730 } | |
731 | |
732 if (out == NULL) { | |
733 return NGX_OK; | |
734 } | |
735 | |
736 return ngx_http_next_body_filter(r, out); | |
737 } | |
738 | |
739 | |
740 static ngx_int_t | |
741 ngx_http_range_multipart_body(ngx_http_request_t *r, | |
742 ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in, | |
743 ngx_http_output_body_filter_pt ngx_http_next_body_filter) | |
744 { | |
745 ngx_buf_t *b, *buf; | |
746 ngx_uint_t i; | |
747 ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; | |
748 ngx_http_range_t *range; | |
749 | |
750 ll = &out; | |
751 buf = in->buf; | |
752 range = ctx->ranges.elts; | |
652 | 753 |
653 for (i = 0; i < ctx->ranges.nelts; i++) { | 754 for (i = 0; i < ctx->ranges.nelts; i++) { |
654 | 755 |
655 /* | 756 /* |
656 * The boundary header of the range: | 757 * The boundary header of the range: |
762 hcl->next = NULL; | 863 hcl->next = NULL; |
763 | 864 |
764 *ll = hcl; | 865 *ll = hcl; |
765 | 866 |
766 return ngx_http_next_body_filter(r, out); | 867 return ngx_http_next_body_filter(r, out); |
767 | |
768 overlapped: | |
769 | |
770 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
771 "range in overlapped buffers"); | |
772 | |
773 return NGX_ERROR; | |
774 } | 868 } |
775 | 869 |
776 | 870 |
777 static ngx_int_t | 871 static ngx_int_t |
778 ngx_http_range_body_early_filter(ngx_http_request_t *r, ngx_chain_t *in) | 872 ngx_http_range_body_early_filter(ngx_http_request_t *r, ngx_chain_t *in) |