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”)抓取到了资源的部分数据”,适用于大文件下载和断点续传;
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>
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>
typedef struct {
off_t
start; //range起始
off_t
end; //range末尾
ngx_str_t
content_range; //一般是文件大小
} ngx_http_range_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;
函数功能:设置header filter为ngx_http_range_header_filter。
函数功能:设置body filter为ngx_http_range_body_filter。
函数功能: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>
函数功能:解析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>
函数功能:填充响应头的Content-Range,格式为Content-Range: bytes 100-300/500。