Mercurial > hg > nginx
comparison src/http/modules/ngx_http_range_filter_module.c @ 501:d4ea69372b94 release-0.1.25
nginx-0.1.25-RELEASE import
*) Bugfix: nginx did run on Linux parisc.
*) Feature: nginx now does not start under FreeBSD if the sysctl
kern.ipc.somaxconn value is too big.
*) Bugfix: if a request was internally redirected by the
ngx_http_index_module module to the ngx_http_proxy_module or
ngx_http_fastcgi_module modules, then the index file was not closed
after request completion.
*) Feature: the "proxy_pass" can be used in location with regular
expression.
*) Feature: the ngx_http_rewrite_filter_module module supports the
condition like "if ($HTTP_USER_AGENT ~ MSIE)".
*) Bugfix: nginx started too slow if the large number of addresses and
text values were used in the "geo" directive.
*) Change: a variable name must be declared as "$name" in the "geo"
directive. The previous variant without "$" is still supported, but
will be removed soon.
*) Feature: the "%{VARIABLE}v" logging parameter.
*) Feature: the "set $name value" directive.
*) Bugfix: gcc 4.0 compatibility.
*) Feature: the --with-openssl-opt=OPTIONS autoconfiguration directive.
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Sat, 19 Mar 2005 12:38:37 +0000 |
parents | src/http/modules/ngx_http_range_filter.c@64d9afb209da |
children | 9b8c906f6e63 |
comparison
equal
deleted
inserted
replaced
500:9a0f304470f5 | 501:d4ea69372b94 |
---|---|
1 | |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_http.h> | |
10 | |
11 | |
12 /* | |
13 * the single part format: | |
14 * | |
15 * "HTTP/1.0 206 Partial Content" CRLF | |
16 * ... header ... | |
17 * "Content-Type: image/jpeg" CRLF | |
18 * "Content-Length: SIZE" CRLF | |
19 * "Content-Range: bytes START-END/SIZE" CRLF | |
20 * CRLF | |
21 * ... data ... | |
22 * | |
23 * | |
24 * the mutlipart format: | |
25 * | |
26 * "HTTP/1.0 206 Partial Content" CRLF | |
27 * ... header ... | |
28 * "Content-Type: multipart/byteranges; boundary=0123456789" CRLF | |
29 * CRLF | |
30 * CRLF | |
31 * "--0123456789" CRLF | |
32 * "Content-Type: image/jpeg" CRLF | |
33 * "Content-Range: bytes START0-END0/SIZE" CRLF | |
34 * CRLF | |
35 * ... data ... | |
36 * CRLF | |
37 * "--0123456789" CRLF | |
38 * "Content-Type: image/jpeg" CRLF | |
39 * "Content-Range: bytes START1-END1/SIZE" CRLF | |
40 * CRLF | |
41 * ... data ... | |
42 * CRLF | |
43 * "--0123456789--" CRLF | |
44 */ | |
45 | |
46 | |
47 typedef struct { | |
48 ngx_str_t boundary_header; | |
49 } ngx_http_range_filter_ctx_t; | |
50 | |
51 | |
52 static ngx_int_t ngx_http_range_header_filter_init(ngx_cycle_t *cycle); | |
53 static ngx_int_t ngx_http_range_body_filter_init(ngx_cycle_t *cycle); | |
54 | |
55 | |
56 static ngx_http_module_t ngx_http_range_header_filter_module_ctx = { | |
57 NULL, /* pre conf */ | |
58 | |
59 NULL, /* create main configuration */ | |
60 NULL, /* init main configuration */ | |
61 | |
62 NULL, /* create server configuration */ | |
63 NULL, /* merge server configuration */ | |
64 | |
65 NULL, /* create location configuration */ | |
66 NULL, /* merge location configuration */ | |
67 }; | |
68 | |
69 | |
70 ngx_module_t ngx_http_range_header_filter_module = { | |
71 NGX_MODULE, | |
72 &ngx_http_range_header_filter_module_ctx, /* module context */ | |
73 NULL, /* module directives */ | |
74 NGX_HTTP_MODULE, /* module type */ | |
75 ngx_http_range_header_filter_init, /* init module */ | |
76 NULL /* init process */ | |
77 }; | |
78 | |
79 | |
80 static ngx_http_module_t ngx_http_range_body_filter_module_ctx = { | |
81 NULL, /* pre conf */ | |
82 | |
83 NULL, /* create main configuration */ | |
84 NULL, /* init main configuration */ | |
85 | |
86 NULL, /* create server configuration */ | |
87 NULL, /* merge server configuration */ | |
88 | |
89 NULL, /* create location configuration */ | |
90 NULL, /* merge location configuration */ | |
91 }; | |
92 | |
93 | |
94 ngx_module_t ngx_http_range_body_filter_module = { | |
95 NGX_MODULE, | |
96 &ngx_http_range_body_filter_module_ctx, /* module context */ | |
97 NULL, /* module directives */ | |
98 NGX_HTTP_MODULE, /* module type */ | |
99 ngx_http_range_body_filter_init, /* init module */ | |
100 NULL /* init process */ | |
101 }; | |
102 | |
103 | |
104 static ngx_http_output_header_filter_pt ngx_http_next_header_filter; | |
105 static ngx_http_output_body_filter_pt ngx_http_next_body_filter; | |
106 | |
107 | |
108 static ngx_int_t | |
109 ngx_http_range_header_filter(ngx_http_request_t *r) | |
110 { | |
111 u_char *p; | |
112 size_t len; | |
113 off_t start, end; | |
114 ngx_int_t rc; | |
115 ngx_uint_t suffix, i; | |
116 ngx_atomic_uint_t boundary; | |
117 ngx_table_elt_t *content_range; | |
118 ngx_http_range_t *range; | |
119 ngx_http_range_filter_ctx_t *ctx; | |
120 | |
121 if (r->http_version < NGX_HTTP_VERSION_10 | |
122 || r->headers_out.status != NGX_HTTP_OK | |
123 || r->headers_out.content_length_n == -1 | |
124 || !r->filter_allow_ranges) | |
125 { | |
126 return ngx_http_next_header_filter(r); | |
127 } | |
128 | |
129 if (r->headers_in.range == NULL | |
130 || r->headers_in.range->value.len < 7 | |
131 || ngx_strncasecmp(r->headers_in.range->value.data, "bytes=", 6) != 0) | |
132 { | |
133 | |
134 r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers); | |
135 if (r->headers_out.accept_ranges == NULL) { | |
136 return NGX_ERROR; | |
137 } | |
138 | |
139 r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1; | |
140 r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges"; | |
141 r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1; | |
142 r->headers_out.accept_ranges->value.data = (u_char *) "bytes"; | |
143 | |
144 return ngx_http_next_header_filter(r); | |
145 } | |
146 | |
147 if (ngx_array_init(&r->headers_out.ranges, r->pool, 2, | |
148 sizeof(ngx_http_range_t)) == NGX_ERROR) | |
149 { | |
150 return NGX_ERROR; | |
151 } | |
152 | |
153 rc = 0; | |
154 range = NULL; | |
155 p = r->headers_in.range->value.data + 6; | |
156 | |
157 for ( ;; ) { | |
158 start = 0; | |
159 end = 0; | |
160 suffix = 0; | |
161 | |
162 while (*p == ' ') { p++; } | |
163 | |
164 if (*p != '-') { | |
165 if (*p < '0' || *p > '9') { | |
166 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | |
167 break; | |
168 } | |
169 | |
170 while (*p >= '0' && *p <= '9') { | |
171 start = start * 10 + *p++ - '0'; | |
172 } | |
173 | |
174 while (*p == ' ') { p++; } | |
175 | |
176 if (*p++ != '-') { | |
177 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | |
178 break; | |
179 } | |
180 | |
181 if (start >= r->headers_out.content_length_n) { | |
182 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | |
183 break; | |
184 } | |
185 | |
186 while (*p == ' ') { p++; } | |
187 | |
188 if (*p == ',' || *p == '\0') { | |
189 range = ngx_array_push(&r->headers_out.ranges); | |
190 if (range == NULL) { | |
191 return NGX_ERROR; | |
192 } | |
193 | |
194 range->start = start; | |
195 range->end = r->headers_out.content_length_n; | |
196 | |
197 if (*p++ != ',') { | |
198 break; | |
199 } | |
200 | |
201 continue; | |
202 } | |
203 | |
204 } else { | |
205 suffix = 1; | |
206 p++; | |
207 } | |
208 | |
209 if (*p < '0' || *p > '9') { | |
210 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | |
211 break; | |
212 } | |
213 | |
214 while (*p >= '0' && *p <= '9') { | |
215 end = end * 10 + *p++ - '0'; | |
216 } | |
217 | |
218 while (*p == ' ') { p++; } | |
219 | |
220 if (*p != ',' && *p != '\0') { | |
221 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | |
222 break; | |
223 } | |
224 | |
225 if (suffix) { | |
226 start = r->headers_out.content_length_n - end; | |
227 end = r->headers_out.content_length_n - 1; | |
228 } | |
229 | |
230 if (start > end) { | |
231 rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; | |
232 break; | |
233 } | |
234 | |
235 range = ngx_array_push(&r->headers_out.ranges); | |
236 if (range == NULL) { | |
237 return NGX_ERROR; | |
238 } | |
239 | |
240 range->start = start; | |
241 | |
242 if (end >= r->headers_out.content_length_n) { | |
243 /* | |
244 * Download Accelerator sends the last byte position | |
245 * that equals to the file length | |
246 */ | |
247 range->end = r->headers_out.content_length_n; | |
248 | |
249 } else { | |
250 range->end = end + 1; | |
251 } | |
252 | |
253 if (*p++ != ',') { | |
254 break; | |
255 } | |
256 } | |
257 | |
258 if (rc) { | |
259 | |
260 /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */ | |
261 | |
262 r->headers_out.status = rc; | |
263 r->headers_out.ranges.nelts = 0; | |
264 | |
265 content_range = ngx_list_push(&r->headers_out.headers); | |
266 if (content_range == NULL) { | |
267 return NGX_ERROR; | |
268 } | |
269 | |
270 r->headers_out.content_range = content_range; | |
271 | |
272 content_range->key.len = sizeof("Content-Range") - 1; | |
273 content_range->key.data = (u_char *) "Content-Range"; | |
274 | |
275 content_range->value.data = ngx_palloc(r->pool, | |
276 sizeof("bytes */") - 1 + NGX_OFF_T_LEN); | |
277 if (content_range->value.data == NULL) { | |
278 return NGX_ERROR; | |
279 } | |
280 | |
281 content_range->value.len = ngx_sprintf(content_range->value.data, | |
282 "bytes */%O", | |
283 r->headers_out.content_length_n) | |
284 - content_range->value.data; | |
285 | |
286 r->headers_out.content_length_n = -1; | |
287 if (r->headers_out.content_length) { | |
288 r->headers_out.content_length->key.len = 0; | |
289 r->headers_out.content_length = NULL; | |
290 } | |
291 | |
292 return rc; | |
293 } | |
294 | |
295 r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; | |
296 | |
297 if (r->headers_out.ranges.nelts == 1) { | |
298 | |
299 content_range = ngx_list_push(&r->headers_out.headers); | |
300 if (content_range == NULL) { | |
301 return NGX_ERROR; | |
302 } | |
303 | |
304 r->headers_out.content_range = content_range; | |
305 | |
306 content_range->key.len = sizeof("Content-Range") - 1; | |
307 content_range->key.data = (u_char *) "Content-Range"; | |
308 | |
309 content_range->value.data = | |
310 ngx_palloc(r->pool, sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN); | |
311 if (content_range->value.data == NULL) { | |
312 return NGX_ERROR; | |
313 } | |
314 | |
315 /* "Content-Range: bytes SSSS-EEEE/TTTT" header */ | |
316 | |
317 content_range->value.len = ngx_sprintf(content_range->value.data, | |
318 "bytes %O-%O/%O", | |
319 range->start, range->end - 1, | |
320 r->headers_out.content_length_n) | |
321 - content_range->value.data; | |
322 | |
323 r->headers_out.content_length_n = range->end - range->start; | |
324 | |
325 return ngx_http_next_header_filter(r); | |
326 } | |
327 | |
328 | |
329 /* TODO: what if no content_type ?? */ | |
330 | |
331 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t)); | |
332 if (ctx == NULL) { | |
333 return NGX_ERROR; | |
334 } | |
335 | |
336 ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); | |
337 | |
338 | |
339 len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN | |
340 + sizeof(CRLF "Content-Type: ") - 1 | |
341 + r->headers_out.content_type->value.len | |
342 + sizeof(CRLF "Content-Range: bytes ") - 1; | |
343 | |
344 if (r->headers_out.charset.len) { | |
345 len += sizeof("; charset=") - 1 + r->headers_out.charset.len; | |
346 } | |
347 | |
348 ctx->boundary_header.data = ngx_palloc(r->pool, len); | |
349 if (ctx->boundary_header.data == NULL) { | |
350 return NGX_ERROR; | |
351 } | |
352 | |
353 boundary = ngx_next_temp_number(0); | |
354 | |
355 /* | |
356 * The boundary header of the range: | |
357 * CRLF | |
358 * "--0123456789" CRLF | |
359 * "Content-Type: image/jpeg" CRLF | |
360 * "Content-Range: bytes " | |
361 */ | |
362 | |
363 if (r->headers_out.charset.len) { | |
364 ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data, | |
365 CRLF "--%0muA" CRLF | |
366 "Content-Type: %V; charset=%V" CRLF | |
367 "Content-Range: bytes ", | |
368 boundary, | |
369 &r->headers_out.content_type->value, | |
370 &r->headers_out.charset) | |
371 - ctx->boundary_header.data; | |
372 | |
373 r->headers_out.charset.len = 0; | |
374 | |
375 } else { | |
376 ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data, | |
377 CRLF "--%0muA" CRLF | |
378 "Content-Type: %V" CRLF | |
379 "Content-Range: bytes ", | |
380 boundary, | |
381 &r->headers_out.content_type->value) | |
382 - ctx->boundary_header.data; | |
383 } | |
384 | |
385 r->headers_out.content_type->value.data = | |
386 ngx_palloc(r->pool, | |
387 sizeof("Content-Type: multipart/byteranges; boundary=") - 1 | |
388 + NGX_ATOMIC_T_LEN); | |
389 | |
390 if (r->headers_out.content_type->value.data == NULL) { | |
391 return NGX_ERROR; | |
392 } | |
393 | |
394 /* "Content-Type: multipart/byteranges; boundary=0123456789" */ | |
395 | |
396 r->headers_out.content_type->value.len = | |
397 ngx_sprintf(r->headers_out.content_type->value.data, | |
398 "multipart/byteranges; boundary=%0muA", | |
399 boundary) | |
400 - r->headers_out.content_type->value.data; | |
401 | |
402 | |
403 /* the size of the last boundary CRLF "--0123456789--" CRLF */ | |
404 | |
405 len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1; | |
406 | |
407 range = r->headers_out.ranges.elts; | |
408 for (i = 0; i < r->headers_out.ranges.nelts; i++) { | |
409 | |
410 /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */ | |
411 | |
412 range[i].content_range.data = | |
413 ngx_palloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4); | |
414 | |
415 if (range[i].content_range.data == NULL) { | |
416 return NGX_ERROR; | |
417 } | |
418 | |
419 range[i].content_range.len = ngx_sprintf(range[i].content_range.data, | |
420 "%O-%O/%O" CRLF CRLF, | |
421 range[i].start, range[i].end - 1, | |
422 r->headers_out.content_length_n) | |
423 - range[i].content_range.data; | |
424 | |
425 len += ctx->boundary_header.len + range[i].content_range.len | |
426 + (size_t) (range[i].end - range[i].start); | |
427 } | |
428 | |
429 r->headers_out.content_length_n = len; | |
430 r->headers_out.content_length = NULL; | |
431 | |
432 return ngx_http_next_header_filter(r); | |
433 } | |
434 | |
435 | |
436 static ngx_int_t | |
437 ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
438 { | |
439 ngx_uint_t i; | |
440 ngx_buf_t *b; | |
441 ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; | |
442 ngx_http_range_t *range; | |
443 ngx_http_range_filter_ctx_t *ctx; | |
444 | |
445 if (r->headers_out.ranges.nelts == 0) { | |
446 return ngx_http_next_body_filter(r, in); | |
447 } | |
448 | |
449 /* | |
450 * the optimized version for the static files only | |
451 * that are passed in the single file buf | |
452 */ | |
453 | |
454 if (in && in->buf->in_file && in->buf->last_buf) { | |
455 range = r->headers_out.ranges.elts; | |
456 | |
457 if (r->headers_out.ranges.nelts == 1) { | |
458 in->buf->file_pos = range->start; | |
459 in->buf->file_last = range->end; | |
460 | |
461 return ngx_http_next_body_filter(r, in); | |
462 } | |
463 | |
464 ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module); | |
465 ll = &out; | |
466 | |
467 for (i = 0; i < r->headers_out.ranges.nelts; i++) { | |
468 | |
469 /* | |
470 * The boundary header of the range: | |
471 * CRLF | |
472 * "--0123456789" CRLF | |
473 * "Content-Type: image/jpeg" CRLF | |
474 * "Content-Range: bytes " | |
475 */ | |
476 | |
477 b = ngx_calloc_buf(r->pool); | |
478 if (b == NULL) { | |
479 return NGX_ERROR; | |
480 } | |
481 | |
482 b->memory = 1; | |
483 b->pos = ctx->boundary_header.data; | |
484 b->last = ctx->boundary_header.data + ctx->boundary_header.len; | |
485 | |
486 hcl = ngx_alloc_chain_link(r->pool); | |
487 if (hcl == NULL) { | |
488 return NGX_ERROR; | |
489 } | |
490 | |
491 hcl->buf = b; | |
492 | |
493 | |
494 /* "SSSS-EEEE/TTTT" CRLF CRLF */ | |
495 | |
496 b = ngx_calloc_buf(r->pool); | |
497 if (b == NULL) { | |
498 return NGX_ERROR; | |
499 } | |
500 | |
501 b->temporary = 1; | |
502 b->pos = range[i].content_range.data; | |
503 b->last = range[i].content_range.data + range[i].content_range.len; | |
504 | |
505 rcl = ngx_alloc_chain_link(r->pool); | |
506 if (rcl == NULL) { | |
507 return NGX_ERROR; | |
508 } | |
509 | |
510 rcl->buf = b; | |
511 | |
512 | |
513 /* the range data */ | |
514 | |
515 b = ngx_calloc_buf(r->pool); | |
516 if (b == NULL) { | |
517 return NGX_ERROR; | |
518 } | |
519 | |
520 b->in_file = 1; | |
521 b->file_pos = range[i].start; | |
522 b->file_last = range[i].end; | |
523 b->file = in->buf->file; | |
524 | |
525 dcl = ngx_alloc_chain_link(r->pool); | |
526 if (dcl == NULL) { | |
527 return NGX_ERROR; | |
528 } | |
529 | |
530 dcl->buf = b; | |
531 | |
532 *ll = hcl; | |
533 hcl->next = rcl; | |
534 rcl->next = dcl; | |
535 ll = &dcl->next; | |
536 } | |
537 | |
538 /* the last boundary CRLF "--0123456789--" CRLF */ | |
539 | |
540 b = ngx_calloc_buf(r->pool); | |
541 if (b == NULL) { | |
542 return NGX_ERROR; | |
543 } | |
544 | |
545 b->temporary = 1; | |
546 b->last_buf = 1; | |
547 | |
548 b->pos = ngx_palloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN | |
549 + sizeof("--" CRLF) - 1); | |
550 if (b->pos == NULL) { | |
551 return NGX_ERROR; | |
552 } | |
553 | |
554 b->last = ngx_cpymem(b->pos, ctx->boundary_header.data, 4 + 10); | |
555 *b->last++ = '-'; *b->last++ = '-'; | |
556 *b->last++ = CR; *b->last++ = LF; | |
557 | |
558 hcl = ngx_alloc_chain_link(r->pool); | |
559 if (hcl == NULL) { | |
560 return NGX_ERROR; | |
561 } | |
562 | |
563 hcl->buf = b; | |
564 hcl->next = NULL; | |
565 | |
566 *ll = hcl; | |
567 | |
568 return ngx_http_next_body_filter(r, out); | |
569 } | |
570 | |
571 /* TODO: alert */ | |
572 | |
573 return ngx_http_next_body_filter(r, in); | |
574 } | |
575 | |
576 | |
577 static ngx_int_t | |
578 ngx_http_range_header_filter_init(ngx_cycle_t *cycle) | |
579 { | |
580 ngx_http_next_header_filter = ngx_http_top_header_filter; | |
581 ngx_http_top_header_filter = ngx_http_range_header_filter; | |
582 | |
583 return NGX_OK; | |
584 } | |
585 | |
586 | |
587 static ngx_int_t | |
588 ngx_http_range_body_filter_init(ngx_cycle_t *cycle) | |
589 { | |
590 ngx_http_next_body_filter = ngx_http_top_body_filter; | |
591 ngx_http_top_body_filter = ngx_http_range_body_filter; | |
592 | |
593 return NGX_OK; | |
594 } |