IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    nginx模块开发(52)—range filter模块分析

    cjhust发表于 2016-03-28 11:14:53
    love 0

    1、知识百科

    HTTP 2XX范围内的状态码表明了“客户端发送的请求已经被服务器接受并且被成功处理了”。

    HTTP/1.1 200 OK是HTTP请求成功后的标准响应,当你在浏览器中打开www.cyberciti.biz后,你通常会得到一个200状态码;

    HTTP/1.1 206状态码表示的是“客户端通过发送范围请求头Range(如”range: bytes=100-500”或”range: bytes=100-500,500-700”)抓取到了资源的部分数据”,适用于大文件下载和断点续传;

    single part format

    de style="margin: 0px; padding: 0px; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; border: none; border-image-source: initial; border-image-slice: initial; border-image-width: initial; border-image-outset: initial; border-image-repeat: initial; border-radius: 3px; background-image: initial; background-attachment: initial; background-color: transparent; background-origin: initial; background-clip: initial;"  >* "HTTP/1.0 206 Partial Content" CRLF
     * ... header ...
     * "Content-Type: image/jpeg" CRLF
     * "Content-Length: SIZE" CRLF
     * "Content-Range: bytes START-END/SIZE" CRLF
     * CRLF
     * ... data ...de>

    multipart format(暂不考虑)

    de style="margin: 0px; padding: 0px; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; border: none; border-image-source: initial; border-image-slice: initial; border-image-width: initial; border-image-outset: initial; border-image-repeat: initial; border-radius: 3px; background-image: initial; background-attachment: initial; background-color: transparent; background-origin: initial; background-clip: initial;"  >* "HTTP/1.0 206 Partial Content" CRLF
     * ... header ...
     * "Content-Type: multipart/byteranges; boundary=0123456789" CRLF
     * CRLF
     * CRLF
     * "--0123456789" CRLF
     * "Content-Type: image/jpeg" CRLF
     * "Content-Range: bytes START0-END0/SIZE" CRLF
     * CRLF
     * ... data ...
     * CRLF
     * "--0123456789" CRLF
     * "Content-Type: image/jpeg" CRLF
     * "Content-Range: bytes START1-END1/SIZE" CRLF
     * CRLF
     * ... data ...
     * CRLF
    * "--0123456789--" CRLF
    de>

    2、数据结构

    ngx_http_range_t

    typedef struct {
        off_t        start;                          //range起始
     off_t        end;                           //range末尾
     ngx_str_t    content_range;      //一般是文件大小
    } ngx_http_range_t;

    ngx_http_range_filter_ctx_t

    typedef struct {
        off_t        offset;                        //偏移,记录当前数据的偏移
     ngx_str_t    boundary_header;
        ngx_array_t  ranges;                // ngx_http_range_t,主请求和子请求共用一个
    } ngx_http_range_filter_ctx_t;

    3、源码分析

    ngx_http_range_header_filter_init(ngx_conf_t *cf)

    函数功能:设置header filter为ngx_http_range_header_filter。

    ngx_http_range_body_filter_init(ngx_conf_t *cf)

    函数功能:设置body filter为ngx_http_range_body_filter。

    ngx_http_range_header_filter(ngx_http_request_t *r)

    函数功能:range header filter。

    de style="margin: 0px; padding: 0px; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; border: none; border-radius: 3px; background-image: initial; background-attachment: initial; background-color: transparent; background-origin: initial; background-clip: initial;"  >static ngx_int_t
    ngx_http_range_header_filter(ngx_http_request_t *r)
    {
        。。。
    
        if (r->http_version < NGX_HTTP_VERSION_10
            || r->headers_out.status != NGX_HTTP_OK   //slice模块会修改返回状态为200
            || (r != r->main && !r->subrequest_ranges)   //子请求&子请求不可以range
            || r->headers_out.content_length_n == -1    
            || !r->allow_ranges)                     //允许range,slice会设置为1
        {
            return ngx_http_next_header_filter(r);
        }
    
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        if (clcf->max_ranges == 0) {   // max_ranges 指令设置,默认是2147483647个
            return ngx_http_next_header_filter(r);
        }
       
        //range头为NULL或错误,正确的range头为”range: bytes=1-5”
        if (r->headers_in.range == NULL
            || r->headers_in.range->value.len < 7
            || ngx_strncasecmp(r->headers_in.range->value.data,
                               (u_char *) "bytes=", 6)
               != 0)
        {
            goto next_filter;
        }
    
        // 请求头有if-Range:  “737060cd8c284d8af7ad3082f209582d”(etag的值)
        if (r->headers_in.if_range) {   
            if_range = &r->headers_in.if_range->value;
    
            if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '"') {
    
                if (r->headers_out.etag == NULL) {
                    goto next_filter;
                }
    
                etag = &r->headers_out.etag->value;
    
                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                               "http ir:%V etag:%V", if_range, etag);
    
                if (if_range->len != etag->len
                    || ngx_strncmp(if_range->data, etag->data, etag->len) != 0)
                {
                    goto next_filter;
                }
    
                goto parse;     //文件内容未改变
            }
    
            //文件内容可能改变,发送整个文件
            if (r->headers_out.last_modified_time == (time_t) -1) {
                goto next_filter;
            }
    
            if_range_time = ngx_parse_http_time(if_range->data, if_range->len);
    
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http ir:%d lm:%d",
                           if_range_time, r->headers_out.last_modified_time);
    
            if (if_range_time != r->headers_out.last_modified_time) {
                goto next_filter;
            }
        }
    
    parse:
        //主、子请求都有可能走到这里来,每次都要分配一个空间?
        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
        if (ctx == NULL) {
            return NGX_ERROR;
        }
    
        //slice header filter会将偏移写到r->headers_out.content_offset=cr.start中
        ctx->offset = r->headers_out.content_offset;   
        if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))
            != NGX_OK)
        {
            return NGX_ERROR;
        }
        
        //slice header filter中,将r->single_range设置为1
        ranges = r->single_range ? 1 : clcf->max_ranges;
    
        //子请求继承主请求的ranges,直接返回NGX_OK
        switch (ngx_http_range_parse(r, ctx, ranges)) {   
    
        case NGX_OK:
            ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
    	
            r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;     //206
            r->headers_out.status_line.len = 0;
    
            if (ctx->ranges.nelts == 1) {
                return ngx_http_range_singlepart_header(r, ctx); 
            }
            
            //多个range头,如”range: bytes=500-600,600-700”
            return ngx_http_range_multipart_header(r, ctx);
    
        case NGX_HTTP_RANGE_NOT_SATISFIABLE:       //格式错误
            return ngx_http_range_not_satisfiable(r);
    
        case NGX_ERROR:                            //内存不足
            return NGX_ERROR;
    
        default: /* NGX_DECLINED */
            break;
        }
    
    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;
        ngx_str_set(&r->headers_out.accept_ranges->key, "Accept-Ranges");
        ngx_str_set(&r->headers_out.accept_ranges->value, "bytes");
    
        return ngx_http_next_header_filter(r);
    }
    de>

    ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx, ngx_uint_t ranges)

    函数功能:解析range头格式。

    de style="margin: 0px; padding: 0px; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; border: none; border-radius: 3px; background-image: initial; background-attachment: initial; background-color: transparent; background-origin: initial; background-clip: initial;"  >
    static ngx_int_t ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx, ngx_uint_t ranges) { … //子请求继承主请求的ranges,然后直接返回 if (r != r->main) { mctx = ngx_http_get_module_ctx(r->main, ngx_http_range_body_filter_module); if (mctx) { ctx->ranges = mctx->ranges; return NGX_OK; } } //主请求,因此只会操作一次“range: bytes=1-40” p = r->headers_in.range->value.data + 6; size = 0; content_length = r->headers_out.content_length_n; //不一定是精确的值 cutoff = NGX_MAX_OFF_T_VALUE / 10; cutlim = NGX_MAX_OFF_T_VALUE % 10; //多个range头,如”range: bytes=500-600,600-700” for ( ;; ) { start = 0; end = 0; suffix = 0; while (*p == ' ') { p++; } if (*p != '-') { if (*p < '0' || *p > '9') { return NGX_HTTP_RANGE_NOT_SATISFIABLE; } while (*p >= '0' && *p <= '9') { if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) { return NGX_HTTP_RANGE_NOT_SATISFIABLE; } start = start * 10 + *p++ - '0'; } while (*p == ' ') { p++; } if (*p++ != '-') { return NGX_HTTP_RANGE_NOT_SATISFIABLE; } while (*p == ' ') { p++; } if (*p == ',' || *p == '\0') { //range头可能是”bytes=xx-” end = content_length; goto found; } } else { suffix = 1; p++; } if (*p < '0' || *p > '9') { return NGX_HTTP_RANGE_NOT_SATISFIABLE; } while (*p >= '0' && *p <= '9') { if (end >= cutoff && (end > cutoff || *p - '0' > cutlim)) { return NGX_HTTP_RANGE_NOT_SATISFIABLE; } end = end * 10 + *p++ - '0'; } while (*p == ' ') { p++; } if (*p != ',' && *p != '\0') { return NGX_HTTP_RANGE_NOT_SATISFIABLE; } if (suffix) { start = content_length - end; end = content_length - 1; } if (end >= content_length) { end = content_length; } else { end++; } found: if (start < end) { range = ngx_array_push(&ctx->ranges); if (range == NULL) { return NGX_ERROR; } range->start = start; range->end = end; size += end - start; if (ranges-- == 0) { return NGX_DECLINED; } } if (*p++ != ',') { break; } } if (ctx->ranges.nelts == 0) { return NGX_HTTP_RANGE_NOT_SATISFIABLE; } if (size > content_length) { return NGX_DECLINED; } return NGX_OK; } de>

    ngx_http_range_singlepart_header(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx)

    函数功能:填充响应头的Content-Range,格式为Content-Range: bytes 100-300/500。


    image

    image

    image

    image

    image

    image



沪ICP备19023445号-2号
友情链接