changeset 2118:c1f589732d8c

split ngx_http_range_filter_module
author Igor Sysoev <igor@sysoev.ru>
date Wed, 23 Jul 2008 16:18:37 +0000
parents 9d62abd76ea5
children 0799703985ef
files src/http/modules/ngx_http_range_filter_module.c
diffstat 1 files changed, 214 insertions(+), 126 deletions(-) [+]
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_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_not_satisfiable(ngx_http_request_t *r);
+
 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,84 +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);
+}
+
+
+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;
 
     /* TODO: what if no content_type ?? */
 
@@ -465,32 +493,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 +546,42 @@ ngx_http_range_body_filter(ngx_http_requ
         return ngx_http_next_body_filter(r, in);
     }
 
-    buf = in->buf;
-
     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;
+    }
+
+    if (ctx->ranges.nelts == 1) {
+
+        /*
+         * the optimized version for the responses
+         * that are passed in the single buffer
+         */
+
+        return ngx_http_range_singlepart_body(r, ctx, in);
+    }
+
+    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 +594,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 +602,55 @@ 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:
 
-        if (buf->in_file) {
-            buf->file_pos = range->start;
-            buf->file_last = range->end;
-        }
+    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                  "range in overlapped buffers");
+
+    return NGX_ERROR;
+}
+
 
-        if (ngx_buf_in_memory(buf)) {
-            buf->pos = buf->start + (size_t) range->start;
-            buf->last = buf->start + (size_t) range->end;
-        }
+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)
+{
+    ngx_buf_t         *buf;
+    ngx_http_range_t  *range;
 
-        return ngx_http_next_body_filter(r, in);
+    buf = in->buf;
+    range = ctx->ranges.elts;
+
+    if (buf->in_file) {
+        buf->file_pos = range->start;
+        buf->file_last = range->end;
     }
 
+    if (ngx_buf_in_memory(buf)) {
+        buf->pos = buf->start + (size_t) range->start;
+        buf->last = buf->start + (size_t) range->end;
+    }
+
+    return ngx_http_next_body_filter(r, 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)
+{
+    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 +766,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;
 }