Mercurial > hg > nginx-ranges
diff src/http/modules/ngx_http_range_filter_module.c @ 392:0b6053502c55 NGINX_0_7_7
nginx 0.7.7
*) Change: now the EAGAIN error returned by connect() is not considered
as temporary error.
*) Change: now the $ssl_client_cert variable value is a certificate
with TAB character intended before each line except first one; an
unchanged certificate is available in the $ssl_client_raw_cert
variable.
*) Feature: the "ask" parameter in the "ssl_verify_client" directive.
*) Feature: byte-range processing improvements.
Thanks to Maxim Dounin.
*) Feature: the "directio" directive.
*) Feature: MacOSX 1.5 sendfile() support.
*) Bugfix: now in MacOSX and Cygwin locations are tested in case
insensitive mode; however, the compare is provided by single-byte
locales only.
*) Bugfix: mail proxy SSL connections hanged, if select, poll, or
/dev/poll methods were used.
*) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Wed, 30 Jul 2008 00:00:00 +0400 |
parents | 984bb0b1399b |
children | 77df96611112 b246022ef454 |
line wrap: on
line diff
--- a/src/http/modules/ngx_http_range_filter_module.c +++ b/src/http/modules/ngx_http_range_filter_module.c @@ -58,6 +58,20 @@ typedef struct { } ngx_http_range_filter_ctx_t; +ngx_int_t ngx_http_range_parse(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx); +static ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx); +static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx); +static ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r); +static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in); +static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in); +static ngx_int_t ngx_http_range_multipart_body(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in); + static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf); static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf); @@ -131,15 +145,8 @@ static ngx_http_output_body_filter_pt static ngx_int_t ngx_http_range_header_filter(ngx_http_request_t *r) { - u_char *p; - size_t len; - off_t start, end; time_t if_range; ngx_int_t rc; - ngx_uint_t suffix, i; - ngx_atomic_uint_t boundary; - ngx_table_elt_t *content_range; - ngx_http_range_t *range; ngx_http_range_filter_ctx_t *ctx; if (r->http_version < NGX_HTTP_VERSION_10 @@ -185,8 +192,54 @@ ngx_http_range_header_filter(ngx_http_re return NGX_ERROR; } - rc = 0; - range = NULL; + rc = ngx_http_range_parse(r, ctx); + + if (rc == NGX_OK) { + + ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); + + r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; + + if (ctx->ranges.nelts == 1) { + return ngx_http_range_singlepart_header(r, ctx); + } + + return ngx_http_range_multipart_header(r, ctx); + } + + if (rc == NGX_HTTP_RANGE_NOT_SATISFIABLE) { + return ngx_http_range_not_satisfiable(r); + } + + /* rc == NGX_ERROR */ + + return rc; + +next_filter: + + r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers); + if (r->headers_out.accept_ranges == NULL) { + return NGX_ERROR; + } + + r->headers_out.accept_ranges->hash = 1; + r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1; + r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges"; + r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1; + r->headers_out.accept_ranges->value.data = (u_char *) "bytes"; + + return ngx_http_next_header_filter(r); +} + + +ngx_int_t +ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx) +{ + u_char *p; + off_t start, end; + ngx_uint_t suffix; + ngx_http_range_t *range; + p = r->headers_in.range->value.data + 6; for ( ;; ) { @@ -198,8 +251,7 @@ ngx_http_range_header_filter(ngx_http_re if (*p != '-') { if (*p < '0' || *p > '9') { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } while (*p >= '0' && *p <= '9') { @@ -209,13 +261,11 @@ ngx_http_range_header_filter(ngx_http_re while (*p == ' ') { p++; } if (*p++ != '-') { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } if (start >= r->headers_out.content_length_n) { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } while (*p == ' ') { p++; } @@ -230,7 +280,7 @@ ngx_http_range_header_filter(ngx_http_re range->end = r->headers_out.content_length_n; if (*p++ != ',') { - break; + return NGX_OK; } continue; @@ -242,8 +292,7 @@ ngx_http_range_header_filter(ngx_http_re } if (*p < '0' || *p > '9') { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } while (*p >= '0' && *p <= '9') { @@ -253,8 +302,7 @@ ngx_http_range_header_filter(ngx_http_re while (*p == ' ') { p++; } if (*p != ',' && *p != '\0') { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } if (suffix) { @@ -263,8 +311,7 @@ ngx_http_range_header_filter(ngx_http_re } if (start > end) { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } range = ngx_array_push(&ctx->ranges); @@ -286,86 +333,65 @@ ngx_http_range_header_filter(ngx_http_re } if (*p++ != ',') { - break; + return NGX_OK; } } +} + + +static ngx_int_t +ngx_http_range_singlepart_header(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx) +{ + ngx_table_elt_t *content_range; + ngx_http_range_t *range; + + content_range = ngx_list_push(&r->headers_out.headers); + if (content_range == NULL) { + return NGX_ERROR; + } - if (rc) { - - /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */ - - r->headers_out.status = rc; - - content_range = ngx_list_push(&r->headers_out.headers); - if (content_range == NULL) { - return NGX_ERROR; - } - - r->headers_out.content_range = content_range; + r->headers_out.content_range = content_range; - content_range->hash = 1; - content_range->key.len = sizeof("Content-Range") - 1; - content_range->key.data = (u_char *) "Content-Range"; + content_range->hash = 1; + content_range->key.len = sizeof("Content-Range") - 1; + content_range->key.data = (u_char *) "Content-Range"; - content_range->value.data = ngx_pnalloc(r->pool, - sizeof("bytes */") - 1 + NGX_OFF_T_LEN); - if (content_range->value.data == NULL) { - return NGX_ERROR; - } - - content_range->value.len = ngx_sprintf(content_range->value.data, - "bytes */%O", - r->headers_out.content_length_n) - - content_range->value.data; - - ngx_http_clear_content_length(r); - - return rc; + content_range->value.data = ngx_pnalloc(r->pool, + sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN); + if (content_range->value.data == NULL) { + return NGX_ERROR; } - ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); - - r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; - - if (ctx->ranges.nelts == 1) { + /* "Content-Range: bytes SSSS-EEEE/TTTT" header */ - content_range = ngx_list_push(&r->headers_out.headers); - if (content_range == NULL) { - return NGX_ERROR; - } - - r->headers_out.content_range = content_range; - - content_range->hash = 1; - content_range->key.len = sizeof("Content-Range") - 1; - content_range->key.data = (u_char *) "Content-Range"; + range = ctx->ranges.elts; - content_range->value.data = - ngx_pnalloc(r->pool, sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN); - if (content_range->value.data == NULL) { - return NGX_ERROR; - } - - /* "Content-Range: bytes SSSS-EEEE/TTTT" header */ + content_range->value.len = ngx_sprintf(content_range->value.data, + "bytes %O-%O/%O", + range->start, range->end - 1, + r->headers_out.content_length_n) + - content_range->value.data; - content_range->value.len = ngx_sprintf(content_range->value.data, - "bytes %O-%O/%O", - range->start, range->end - 1, - r->headers_out.content_length_n) - - content_range->value.data; + r->headers_out.content_length_n = range->end - range->start; - r->headers_out.content_length_n = range->end - range->start; - - if (r->headers_out.content_length) { - r->headers_out.content_length->hash = 0; - r->headers_out.content_length = NULL; - } - - return ngx_http_next_header_filter(r); + if (r->headers_out.content_length) { + r->headers_out.content_length->hash = 0; + r->headers_out.content_length = NULL; } + return ngx_http_next_header_filter(r); +} - /* TODO: what if no content_type ?? */ + +static ngx_int_t +ngx_http_range_multipart_header(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx) +{ + size_t len; + ngx_uint_t i; + ngx_http_range_t *range; + ngx_atomic_uint_t boundary; len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof(CRLF "Content-Type: ") - 1 @@ -403,7 +429,7 @@ ngx_http_range_header_filter(ngx_http_re r->headers_out.charset.len = 0; - } else { + } else if (r->headers_out.content_type.len) { ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data, CRLF "--%0muA" CRLF "Content-Type: %V" CRLF @@ -411,6 +437,13 @@ ngx_http_range_header_filter(ngx_http_re boundary, &r->headers_out.content_type) - ctx->boundary_header.data; + + } else { + ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data, + CRLF "--%0muA" CRLF + "Content-Range: bytes ", + boundary) + - ctx->boundary_header.data; } r->headers_out.content_type.data = @@ -465,32 +498,47 @@ ngx_http_range_header_filter(ngx_http_re } return ngx_http_next_header_filter(r); +} -next_filter: - r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers); - if (r->headers_out.accept_ranges == NULL) { +static ngx_int_t +ngx_http_range_not_satisfiable(ngx_http_request_t *r) +{ + ngx_table_elt_t *content_range; + + r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE; + + content_range = ngx_list_push(&r->headers_out.headers); + if (content_range == NULL) { return NGX_ERROR; } - r->headers_out.accept_ranges->hash = 1; - r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1; - r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges"; - r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1; - r->headers_out.accept_ranges->value.data = (u_char *) "bytes"; + r->headers_out.content_range = content_range; + + content_range->hash = 1; + content_range->key.len = sizeof("Content-Range") - 1; + content_range->key.data = (u_char *) "Content-Range"; - return ngx_http_next_header_filter(r); + content_range->value.data = ngx_pnalloc(r->pool, + sizeof("bytes */") - 1 + NGX_OFF_T_LEN); + if (content_range->value.data == NULL) { + return NGX_ERROR; + } + + content_range->value.len = ngx_sprintf(content_range->value.data, + "bytes */%O", + r->headers_out.content_length_n) + - content_range->value.data; + + ngx_http_clear_content_length(r); + + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } static ngx_int_t ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { - off_t start, last; - ngx_buf_t *b, *buf; - ngx_uint_t i; - ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; - ngx_http_range_t *range; ngx_http_range_filter_ctx_t *ctx; if (in == NULL) { @@ -503,17 +551,40 @@ ngx_http_range_body_filter(ngx_http_requ return ngx_http_next_body_filter(r, in); } - buf = in->buf; + if (ctx->ranges.nelts == 1) { + return ngx_http_range_singlepart_body(r, ctx, in); + } + + /* + * multipart ranges are supported only if whole body is in a single buffer + */ if (ngx_buf_special(in->buf)) { return ngx_http_next_body_filter(r, in); } + if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) { + return NGX_ERROR; + } + + return ngx_http_range_multipart_body(r, ctx, in); +} + + +static ngx_int_t +ngx_http_range_test_overlapped(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in) +{ + off_t start, last; + ngx_buf_t *buf; + ngx_uint_t i; + ngx_http_range_t *range; + if (ctx->offset) { goto overlapped; } - range = ctx->ranges.elts; + buf = in->buf; if (!buf->last_buf) { @@ -526,6 +597,7 @@ ngx_http_range_body_filter(ngx_http_requ last = buf->last - buf->start + ctx->offset; } + range = ctx->ranges.elts; for (i = 0; i < ctx->ranges.nelts; i++) { if (start > range[i].start || last < range[i].end) { goto overlapped; @@ -533,29 +605,111 @@ ngx_http_range_body_filter(ngx_http_requ } } - /* - * the optimized version for the responses - * that are passed in the single buffer - */ - ctx->offset = ngx_buf_size(buf); - if (ctx->ranges.nelts == 1) { + return NGX_OK; + +overlapped: + + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "range in overlapped buffers"); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_range_singlepart_body(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in) +{ + off_t start, last; + ngx_buf_t *buf; + ngx_chain_t *out, *cl, **ll; + ngx_http_range_t *range; + + out = NULL; + ll = &out; + range = ctx->ranges.elts; - if (buf->in_file) { - buf->file_pos = range->start; - buf->file_last = range->end; + for (cl = in; cl; cl = cl->next) { + + buf = cl->buf; + + start = ctx->offset; + last = ctx->offset + ngx_buf_size(buf); + + ctx->offset = last; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http range body buf: %O-%O", start, last); + + if (ngx_buf_special(buf)) { + *ll = cl; + ll = &cl->next; + continue; + } + + if (range->end <= start || range->start >= last) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http range body skip"); + + buf->pos = buf->last; + continue; } - if (ngx_buf_in_memory(buf)) { - buf->pos = buf->start + (size_t) range->start; - buf->last = buf->start + (size_t) range->end; + if (range->start > start) { + + if (buf->in_file) { + buf->file_pos += range->start - start; + } + + if (ngx_buf_in_memory(buf)) { + buf->pos += (size_t) (range->start - start); + } } - return ngx_http_next_body_filter(r, in); + if (range->end <= last) { + + if (buf->in_file) { + buf->file_last -= last - range->end; + } + + if (ngx_buf_in_memory(buf)) { + buf->last -= (size_t) (last - range->end); + } + + buf->last_buf = 1; + *ll = cl; + cl->next = NULL; + + break; + } + + *ll = cl; + ll = &cl->next; } + if (out == NULL) { + return NGX_OK; + } + + return ngx_http_next_body_filter(r, out); +} + + +static ngx_int_t +ngx_http_range_multipart_body(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in) +{ + ngx_buf_t *b, *buf; + ngx_uint_t i; + ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; + ngx_http_range_t *range; + ll = &out; + buf = in->buf; + range = ctx->ranges.elts; for (i = 0; i < ctx->ranges.nelts; i++) { @@ -671,13 +825,6 @@ ngx_http_range_body_filter(ngx_http_requ *ll = hcl; return ngx_http_next_body_filter(r, out); - -overlapped: - - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "range in overlapped buffers"); - - return NGX_ERROR; }