static ngx_int_t ngx_http_breach_header_filter(ngx_http_request_t *r) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"BREACH HEADER FILTER Start!."); ngx_http_breach_conf_t *conf; conf = ngx_http_get_module_loc_conf (r, ngx_http_breach_filter_module); if ((r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_FORBIDDEN && r->headers_out.status != NGX_HTTP_NOT_FOUND) || r->header_only || r->headers_out.content_type.len == 0 || (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) || conf->enable == 0) { return ngx_http_next_header_filter(r); } if (ngx_strncasecmp(r->headers_out.content_type.data, (u_char *)"text/html", sizeof("text/html") - 1) != 0) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"BREACH HEADER FILTER: this is not text/html content!."); return ngx_http_next_header_filter(r); } ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"BREACH HEADER FILTER: end!."); return ngx_http_next_header_filter(r); }
ngx_int_t ngx_http_echo_send_header_if_needed(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) { ngx_int_t rc; ngx_http_echo_loc_conf_t *elcf; if (!r->header_sent && !ctx->header_sent) { elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); r->headers_out.status = (ngx_uint_t) elcf->status; if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); rc = ngx_http_send_header(r); ctx->header_sent = 1; return rc; } return NGX_OK; }
static ngx_int_t ngx_http_dynamic_etags_header_filter(ngx_http_request_t *r) { ngx_http_dynamic_etags_module_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_dynamic_etags_module); if (ctx) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_dynamic_etags_module_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_dynamic_etags_module); ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); r->main_filter_need_in_memory = 1; r->filter_need_in_memory = 1; return NGX_OK; }
static ngx_int_t ngx_http_addition_header_filter(ngx_http_request_t *r) { ngx_http_addition_ctx_t *ctx; ngx_http_addition_conf_t *conf; if (r->headers_out.status != NGX_HTTP_OK || r != r->main) { return ngx_http_next_header_filter(r); } conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module); if (conf->before_body.len == 0 && conf->after_body.len == 0) { return ngx_http_next_header_filter(r); } if (ngx_http_test_content_type(r, &conf->types) == NULL) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_addition_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_addition_filter_module); ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_foo_header_filter(ngx_http_request_t *r) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "Header filter add header"); ngx_table_elt_t *h; /* * The filter handler adds "X-Foo: foo" header * to every HTTP 200 response */ if (r->headers_out.status != NGX_HTTP_OK) { return ngx_http_next_header_filter(r); } h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NGX_ERROR; } h->hash = 1; ngx_str_set(&h->key, "X-Foo"); ngx_str_set(&h->value, "foo"); ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); ngx_http_weak_etag(r); return ngx_http_next_header_filter(r); }
ngx_int_t ngx_http_lua_send_header_if_needed(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { if ( ! ctx->headers_sent ) { if (r->headers_out.status == 0) { r->headers_out.status = NGX_HTTP_OK; } if (! ctx->headers_set && ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (! ctx->headers_set) { ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); } if (r->http_version >= NGX_HTTP_VERSION_11) { /* Send response headers for HTTP version <= 1.0 elsewhere */ ctx->headers_sent = 1; return ngx_http_send_header(r); } } return NGX_OK; }
static ngx_int_t ngx_http_gunzip_header_filter(ngx_http_request_t *r) { ngx_http_gunzip_ctx_t *ctx; ngx_http_gunzip_conf_t *conf; conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module); /* TODO support multiple content-codings */ /* TODO ignore content encoding? */ if (conf->enable == NGX_HTTP_GUNZIP_OFF || r->headers_out.content_encoding == NULL || r->headers_out.content_encoding->value.len != 4 || ngx_strncasecmp(r->headers_out.content_encoding->value.data, (u_char *) "gzip", 4) != 0) { return ngx_http_next_header_filter(r); } r->gzip_vary = 1; if (conf->enable == NGX_HTTP_GUNZIP_ON) { if (!r->gzip_tested) { if (ngx_http_gzip_ok(r) == NGX_OK) { return ngx_http_next_header_filter(r); } } else if (r->gzip_ok) { return ngx_http_next_header_filter(r); } } else if (conf->enable == NGX_HTTP_GUNZIP_ALWAYS && ngx_http_test_content_type(r, &conf->types) == NULL) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gunzip_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_gunzip_filter_module); ctx->request = r; r->filter_need_in_memory = 1; r->headers_out.content_encoding->hash = 0; r->headers_out.content_encoding = NULL; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); ngx_http_weak_etag(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r) { if (r->headers_out.status != NGX_HTTP_OK || r != r->main || r->disable_not_modified) { return ngx_http_next_header_filter(r); } if (r->headers_in.if_unmodified_since && !ngx_http_test_if_unmodified(r)) { return ngx_http_filter_finalize_request(r, NULL, NGX_HTTP_PRECONDITION_FAILED); } if (r->headers_in.if_match && !ngx_http_test_if_match(r, r->headers_in.if_match, 0)) { return ngx_http_filter_finalize_request(r, NULL, NGX_HTTP_PRECONDITION_FAILED); } if (r->headers_in.if_modified_since || r->headers_in.if_none_match) { if (r->headers_in.if_modified_since && ngx_http_test_if_modified(r)) { return ngx_http_next_header_filter(r); } if (r->headers_in.if_none_match && !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1)) { return ngx_http_next_header_filter(r); } /* not modified */ r->headers_out.status = NGX_HTTP_NOT_MODIFIED; r->headers_out.status_line.len = 0; r->headers_out.content_type.len = 0; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); if (r->headers_out.content_encoding) { r->headers_out.content_encoding->hash = 0; r->headers_out.content_encoding = NULL; } return ngx_http_next_header_filter(r); } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_trim_header_filter(ngx_http_request_t *r) { ngx_int_t rc; ngx_str_t flag; ngx_http_trim_ctx_t *ctx; ngx_http_trim_loc_conf_t *conf; conf = ngx_http_get_module_loc_conf(r, ngx_http_trim_filter_module); if (!conf->trim_enable || r->headers_out.status != NGX_HTTP_OK || (r->method & NGX_HTTP_HEAD) || r->headers_out.content_length_n == 0 || (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) || ngx_http_test_content_type(r, &conf->types) == NULL) { return ngx_http_next_header_filter(r); } rc = ngx_http_arg(r, (u_char *) NGX_HTTP_TRIM_FLAG, sizeof(NGX_HTTP_TRIM_FLAG) - 1, &flag); if(rc == NGX_OK && flag.len == sizeof("off") - 1 && ngx_strncmp(flag.data, "off", sizeof("off") - 1) == 0) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_trim_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ctx->prev = ' '; ctx->first_line = 1; ngx_http_set_ctx(r, ctx, ngx_http_trim_filter_module); r->main_filter_need_in_memory = 1; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r) { time_t ims; ngx_http_core_loc_conf_t *clcf; if (r->headers_out.status != NGX_HTTP_OK || r != r->main || r->headers_in.if_modified_since == NULL || r->headers_out.last_modified_time == -1) { return ngx_http_next_header_filter(r); } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) { return ngx_http_next_header_filter(r); } ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http ims:%d lm:%d", ims, r->headers_out.last_modified_time); if (ims != r->headers_out.last_modified_time) { if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT || ims < r->headers_out.last_modified_time) { return ngx_http_next_header_filter(r); } } r->headers_out.status = NGX_HTTP_NOT_MODIFIED; r->headers_out.status_line.len = 0; r->headers_out.content_type.len = 0; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); if (r->headers_out.content_encoding) { r->headers_out.content_encoding->hash = 0; r->headers_out.content_encoding = NULL; } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_srcache_send_not_modified(ngx_http_request_t *r) { r->headers_out.status = NGX_HTTP_NOT_MODIFIED; r->headers_out.status_line.len = 0; r->headers_out.content_type.len = 0; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); if (r->headers_out.content_encoding) { r->headers_out.content_encoding->hash = 0; r->headers_out.content_encoding = NULL; } return ngx_http_srcache_next_header_filter(r); }
static ngx_int_t ngx_http_echo_header_filter(ngx_http_request_t *r) { ngx_http_echo_loc_conf_t *conf; ngx_http_echo_ctx_t *ctx; dd("We're in the header filter..."); ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); /* XXX we should add option to insert contents for responses * of non-200 status code here... */ /* if (r->headers_out.status != NGX_HTTP_OK) { if (ctx != NULL) { ctx->skip_filter = 1; } return ngx_http_echo_next_header_filter(r); } */ conf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); if (conf->before_body_cmds == NULL && conf->after_body_cmds == NULL) { if (ctx != NULL) { ctx->skip_filter = 1; } return ngx_http_echo_next_header_filter(r); } if (ctx == NULL) { ctx = ngx_http_echo_create_ctx(r); if (ctx == NULL) { return NGX_ERROR; } ctx->headers_sent = 1; ngx_http_set_ctx(r, ctx, ngx_http_echo_module); } /* enable streaming here (use chunked encoding) */ ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_echo_next_header_filter(r); }
static ngx_int_t ngx_http_echo_header_filter(ngx_http_request_t *r) { ngx_http_echo_loc_conf_t *conf; ngx_http_echo_ctx_t *ctx; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "echo header filter, uri \"%V?%V\"", &r->uri, &r->args); ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); /* XXX we should add option to insert contents for responses * of non-200 status code here... */ /* if (r->headers_out.status != NGX_HTTP_OK) { if (ctx != NULL) { ctx->skip_filter = 1; } return ngx_http_echo_next_header_filter(r); } */ conf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); if (conf->before_body_cmds == NULL && conf->after_body_cmds == NULL) { if (ctx != NULL) { ctx->skip_filter = 1; } return ngx_http_echo_next_header_filter(r); } if (ctx == NULL) { ctx = ngx_http_echo_create_ctx(r); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_echo_module); } /* enable streaming here (use chunked encoding) */ ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_echo_next_header_filter(r); }
ngx_int_t ngx_http_echo_send_header_if_needed(ngx_http_request_t* r) { if (!r->header_sent) { r->headers_out.status = 200; if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_send_header(r); } return NGX_OK; }
ngx_int_t ngx_http_secure_token_init_body_filter(ngx_http_request_t *r, ngx_str_t* token) { ngx_http_secure_token_loc_conf_t *conf; ngx_http_secure_token_ctx_t* ctx; body_processor_t* processor; conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_token_filter_module); // Note: content_type_lowcase is already initialized since we called ngx_http_test_content_type processor = ngx_hash_find(&conf->processors_hash, r->headers_out.content_type_hash, r->headers_out.content_type_lowcase, r->headers_out.content_type_len); if (processor == NULL) { return NGX_DONE; } // add the token to all the URLs in the response ctx = ngx_pcalloc(r->pool, sizeof(*ctx)); if (ctx == NULL) { return NGX_ERROR; } ctx->token = *token; ctx->process = processor->process; ctx->processor_context_offset = processor->processor_context_offset; ctx->processor_params = processor->processor_params; ngx_http_set_ctx(r, ctx, ngx_http_secure_token_filter_module); ctx->last_out = &ctx->out; r->filter_need_in_memory = 1; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); ngx_http_clear_etag(r); return NGX_OK; }
static ngx_int_t ngx_http_no_newlines_header_filter (ngx_http_request_t *r) { ngx_http_no_newlines_ctx_t *ctx; /* to maintain state */ ngx_http_no_newlines_conf_t *conf; /* to check whether module is enabled or not */ conf = ngx_http_get_module_loc_conf (r, ngx_http_no_newlines_module); /* step 1: decide whether to operate */ if ((r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_FORBIDDEN && r->headers_out.status != NGX_HTTP_NOT_FOUND) || r->header_only || r->headers_out.content_type.len == 0 || (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) || conf->enable == 0) { return ngx_http_next_header_filter(r); } if (ngx_strncasecmp(r->headers_out.content_type.data, (u_char *)"text/html", sizeof("text/html") - 1) != 0) { return ngx_http_next_header_filter(r); } /* step 2: operate on the header */ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_no_newlines_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_no_newlines_module); ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); r->main_filter_need_in_memory = 1; /* step 3: call the next filter */ return ngx_http_next_header_filter(r); }
ngx_int_t ngx_http_echo_send_header_if_needed(ngx_http_request_t* r, ngx_http_echo_ctx_t *ctx) { /* ngx_int_t rc; */ if ( ! ctx->headers_sent ) { r->headers_out.status = NGX_HTTP_OK; if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); if (r->http_version >= NGX_HTTP_VERSION_11) { ctx->headers_sent = 1; return ngx_http_send_header(r); } } return NGX_OK; }
static ngx_int_t ngx_http_static_etags_header_filter(ngx_http_request_t *r) { char new_string[100]; char buffer[30]; int status; ngx_log_t *log; u_char *p; size_t root; ngx_str_t path; ngx_http_static_etags_loc_conf_t *loc_conf; struct stat stat_result; log = r->connection->log; loc_conf = ngx_http_get_module_loc_conf( r, ngx_http_static_etags_module ); if ( 1 == loc_conf->FileETag ) { p = ngx_http_map_uri_to_path( r, &path, &root, 0 ); status = stat( (char *) path.data, &stat_result ); if ( NULL == p ) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if ( 0 == status) { ngx_memzero(&new_string, 100); strcat(new_string, "\""); if (loc_conf->etag_options->MD5) { int ret; char md5sum[33]; ret = ngx_http_static_etags_md5((unsigned int) stat_result.st_size, (char *) path.data, (char *) &md5sum, (ngx_log_t *) log); if (ret) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "MD5ERROR Return: %s", md5sum); return NGX_HTTP_INTERNAL_SERVER_ERROR; } strcat(new_string, (char *) md5sum); } if (loc_conf->etag_options->Size) { sprintf(buffer, "%x", (unsigned int) stat_result.st_size); strcat(new_string, (char *) buffer); } if (loc_conf->etag_options->MTime) { sprintf(buffer, "%x", (unsigned int ) stat_result.st_mtime); strcat(new_string, (char *) buffer); } if (loc_conf->etag_options->INode) { sprintf(buffer, "%x", (unsigned int ) stat_result.st_ino); strcat(new_string, (char *) buffer); } strcat(new_string, "\""); r->headers_out.etag = ngx_list_push(&r->headers_out.headers); if (r->headers_out.etag == NULL) { return NGX_ERROR; } ngx_table_elt_t *header; int nelts = r->headers_in.headers.part.nelts; header = r->headers_in.headers.part.elts; int i; for (i = 0; i < nelts; i++) { if (ngx_strncmp(header[i].key.data, "If-None-Match", strlen ("If-None-Match")) == 0) { if(ngx_strncmp(header[i].value.data, new_string, strlen (new_string)) == 0) { r->headers_out.status = NGX_HTTP_NOT_MODIFIED; r->headers_out.status_line.len = 0; r->headers_out.content_type.len = 0; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_next_header_filter(r); } } } r->headers_out.etag->hash = 1; r->headers_out.etag->key.len = sizeof("Etag") - 1; r->headers_out.etag->key.data = (u_char *) "Etag"; r->headers_out.etag->value.len = strlen( new_string ); r->headers_out.etag->value.data = (u_char *) new_string; } else { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Could not stat(): %s", path.data); return NGX_HTTP_INTERNAL_SERVER_ERROR; } } return ngx_http_next_header_filter(r); }
/* {If-None-Match和ETag , If-Modified-Since和Last-Modified If-Modified-Since(浏览器) = Last-Modified(服务器) 作用:浏览器端第一次访问获得服务器的Last-Modified,第2次访问把浏览器端缓存页面的最后修改时间发送到服务器去,服务器会把这 个时间与服务器上实际文件的最后修改时间进行对比。如果时间一致,那么返回304,客户端就直接使用本地缓存文件。如果时间不一致,就 会返回200和新的文件内容。客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示在浏览器中. If-None-Match(浏览器) = ETag(服务器) 作用: If-None-Match和ETag一起工作,工作原理是在HTTP Response中添加ETag信息。 当用户再次请求该资源时,将在HTTP Request 中加入If-None-Match 信息(ETag的值)。如果服务器验证资源的ETag没有改变(该资源没有更新),将返回一个304状态告诉客户端使用本地缓存文件。否则将返回200状态和新的资源和Etag. } { ETags和If-None-Match是一种常用的判断资源是否改变的方法。类似于Last-Modified和HTTP-If-Modified-Since。但是有所不同的是Last-Modified和HTTP-If-Modified-Since只判断资源的最后修改时间,而ETags和If-None-Match可以是资源任何的任何属性。 ETags和If-None-Match的工作原理是在HTTPResponse中添加ETags信息。当客户端再次请求该资源时,将在HTTPRequest中加入If-None-Match信息(ETags的值)。如果服务器验证资源的ETags没有改变(该资源没有改变),将返回一个304状态;否则,服务器将返回200状态,并返回该资源和新的ETags。 } { http响应Last-Modified和ETag 基础知识 1) 什么是”Last-Modified”? 在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记此文件在服务期端 最后被修改的时间,格式类似这样:Last-Modified: Fri, 12 May 2006 18:53:33 GMT 客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since 报头,询问该时间之后文件是否有被修改过: If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT 如果服务器端的资源没有变化,则自动返回 HTTP 304 (Not Changed.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改 变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。 2) 什么是”Etag”? HTTP 协议规格说明定义ETag为“被请求变量的实体值” (参见 ―― 章节 14.19)。 另一种说法是,ETag是一个可以与Web资源关联的记号(token)。典型的Web资源可以一个Web页,但也可能是JSON或XML文档。服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式: ETag: "50b1c1d4f775c61:df3" 客户端的查询更新格式是这样的: If-None-Match: W/"50b1c1d4f775c61:df3" 如果ETag没改变,则返回状态304然后不返回,这也和Last-Modified一样。本人测试Etag主要在断点下载时比较有用。 Last-Modified和Etags如何帮助提高性能? 聪明的开发者会把Last-Modified 和ETags请求的http报头一起使用,这样可利用客户端(例如浏览器)的缓存。因为服务器首先产生 Last-Modified/Etag标记,服务器可在稍后使用它来判断页面是否已经被修改。本质上,客户端通过将该记号传回服务器要求服务器验证其(客户端)缓存。过程如下: 1.客户端请求一个页面(A)。 2.服务器返回页面A,并在给A加上一个Last-Modified/ETag。 3.客户端展现该页面,并将页面连同Last-Modified/ETag一起缓存。 4.客户再次请求页面A,并将上次请求时服务器返回的Last-Modified/ETag一起传递给服务器。 5.服务器检查该Last-Modified或ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。 } */ static ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r) { if (r->headers_out.status != NGX_HTTP_OK //只有返回类型为OK的才进行304 not modified判断 || r != r->main || r->disable_not_modified) { return ngx_http_next_header_filter(r); } /* If-Unmodified-Since: 从字面上看, 意思是: 如果从某个时间点算起, 文件没有被修改..... 1. 如果没有被修改: 则开始`继续'传送文件: 服务器返回: 200 OK 2. 如果文件被修改: 则不传输, 服务器返回: 412 Precondition failed (预处理错误) 用途:断点续传(一般会指定Range参数). 要想断点续传, 那么文件就一定不能被修改, 否则就不是同一个文件了 */ if (r->headers_in.if_unmodified_since && !ngx_http_test_if_unmodified(r)) /* 如果从某个时间点算起, 文件被修改了,客户端请求行中带有if_unmodified_since,表示如果文件如果从某个时间段起没有被修改, 则继续200 OK传送给客户端包体。但是现在文件却被修改了,因此返回错误 */ { return ngx_http_filter_finalize_request(r, NULL, NGX_HTTP_PRECONDITION_FAILED); } if (r->headers_in.if_match && !ngx_http_test_if_match(r, r->headers_in.if_match, 0)) { return ngx_http_filter_finalize_request(r, NULL, NGX_HTTP_PRECONDITION_FAILED); } if (r->headers_in.if_modified_since || r->headers_in.if_none_match) { if (r->headers_in.if_modified_since && ngx_http_test_if_modified(r)) //文件有发生了修改 { return ngx_http_next_header_filter(r); } if (r->headers_in.if_none_match && !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1)) //etag不匹配,说明文件也发生了修改 { return ngx_http_next_header_filter(r); } /* not modified */ //回送304表示文件没有修改 r->headers_out.status = NGX_HTTP_NOT_MODIFIED; r->headers_out.status_line.len = 0; r->headers_out.content_type.len = 0; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); if (r->headers_out.content_encoding) { r->headers_out.content_encoding->hash = 0; r->headers_out.content_encoding = NULL; } return ngx_http_next_header_filter(r); } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_gunzip_header_filter(ngx_http_request_t *r) { ngx_http_gunzip_ctx_t *ctx; ngx_http_gunzip_conf_t *conf; conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module); /* TODO support multiple content-codings */ /* TODO always gunzip - due to configuration or module request */ /* TODO ignore content encoding? */ if (!conf->enable || r->headers_out.content_encoding == NULL || r->headers_out.content_encoding->value.len != 4 || ngx_strncasecmp(r->headers_out.content_encoding->value.data, (u_char *) "gzip", 4) != 0) { return ngx_http_next_header_filter(r); } #if (nginx_version >= 8025 || (nginx_version >= 7065 && nginx_version < 8000)) r->gzip_vary = 1; if (!r->gzip_tested) { if (ngx_http_gzip_ok(r) == NGX_OK) { return ngx_http_next_header_filter(r); } } else if (!r->gzip_ok) { return ngx_http_next_header_filter(r); } #else if (ngx_http_gzip_ok(r) == NGX_OK) { return ngx_http_next_header_filter(r); } #endif ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gunzip_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_gunzip_filter_module); ctx->request = r; r->filter_need_in_memory = 1; r->headers_out.content_encoding->hash = 0; r->headers_out.content_encoding = NULL; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_xss_header_filter(ngx_http_request_t *r) { ngx_http_xss_ctx_t *ctx; ngx_http_xss_conf_t *conf; ngx_str_t callback; u_char *p, *src, *dst; if (r->headers_out.status != NGX_HTTP_OK || r != r->main) { dd("skipped: status not 200 or in subrequest"); return ngx_http_next_header_filter(r); } conf = ngx_http_get_module_loc_conf(r, ngx_http_xss_filter_module); if ( ! conf->get_enabled || r->method != NGX_HTTP_GET) { dd("skipped: get_enabled disabled or the current method is not GET"); return ngx_http_next_header_filter(r); } if (conf->callback_arg.len == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: xss_get is enabled but no xss_callback_arg specified"); return ngx_http_next_header_filter(r); } if (ngx_http_test_content_type(r, &conf->input_types) == NULL) { dd("skipped: content type test not passed"); return ngx_http_next_header_filter(r); } if (ngx_http_arg(r, conf->callback_arg.data, conf->callback_arg.len, &callback) != NGX_OK) { dd("skipped: no callback arg found in the current request: %.*s", conf->callback_arg.len, conf->callback_arg.data); return ngx_http_next_header_filter(r); } p = ngx_palloc(r->pool, callback.len); if (p == NULL) { return NGX_ERROR; } src = callback.data; dst = p; ngx_unescape_uri(&dst, &src, callback.len, NGX_UNESCAPE_URI_COMPONENT); if (src != callback.data + callback.len) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: unescape uri: input data not consumed completely"); return NGX_ERROR; } callback.data = p; callback.len = dst - p; if (ngx_http_xss_test_callback((char *) callback.data, callback.len) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: bad callback argument: \"%V\"", &callback); return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xss_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } /* * set by ngx_pcalloc(): * * ctx->callback = { 0, NULL }; * conf->before_body_sent = 0; */ ctx->callback = callback; ngx_http_set_ctx(r, ctx, ngx_http_xss_filter_module); r->headers_out.content_type = conf->output_type; r->headers_out.content_type_len = conf->output_type.len; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_brotli_header_filter(ngx_http_request_t *r) { ngx_table_elt_t *h, *ae; ngx_http_brotli_ctx_t *ctx; ngx_http_brotli_conf_t *conf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http brotli header filter"); conf = ngx_http_get_module_loc_conf(r, ngx_http_brotli_filter_module); if (!conf->enable || (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_FORBIDDEN && r->headers_out.status != NGX_HTTP_NOT_FOUND) || (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) || (r->headers_out.content_length_n != -1 && r->headers_out.content_length_n < conf->min_length) || ngx_http_test_content_type(r, &conf->types) == NULL || r->header_only) { return ngx_http_next_header_filter(r); } /* Check that brotli is supported. We do not check possible q value * if brotli is supported it takes precendence over gzip if size > * brotli_min_length */ ae = r->headers_in.accept_encoding; if(!ae) { return ngx_http_next_header_filter(r); } /* Since there is no reason for the br string to be present * unless brotli is accepted either as "br" or "brotli" we * just check for "br" */ if (!ngx_strstrn(ae->value.data, "br", 1)) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_brotli_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } if (ngx_strstrn(ae->value.data, "brotli", 5)) { ctx->br = 0; } else { ctx->br = 1; } #if (NGX_HTTP_GZIP) r->gzip_vary = 1; /* Make sure gzip does not execute */ r->gzip_tested = 1; r->gzip_ok = 0; #endif ngx_http_set_ctx(r, ctx, ngx_http_brotli_filter_module); ctx->request = r; h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NGX_ERROR; } h->hash = 1; ngx_str_set(&h->key, "Content-Encoding"); if (ctx->br) { ngx_str_set(&h->value, "br"); } else { ngx_str_set(&h->value, "brotli"); } r->headers_out.content_encoding = h; r->main_filter_need_in_memory = 1; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); ngx_http_weak_etag(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_send_refresh(ngx_http_request_t *r) { u_char *p, *location; size_t len, size; uintptr_t escape; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out; len = r->headers_out.location->value.len; location = r->headers_out.location->value.data; escape = 2 * ngx_escape_uri(NULL, location, len, NGX_ESCAPE_REFRESH); size = sizeof(ngx_http_msie_refresh_head) - 1 + escape + len + sizeof(ngx_http_msie_refresh_tail) - 1; r->err_status = NGX_HTTP_OK; r->headers_out.content_type_len = sizeof("text/html") - 1; ngx_str_set(&r->headers_out.content_type, "text/html"); r->headers_out.content_type_lowcase = NULL; r->headers_out.location->hash = 0; r->headers_out.location = NULL; r->headers_out.content_length_n = size; if (r->headers_out.content_length) { r->headers_out.content_length->hash = 0; r->headers_out.content_length = NULL; } ngx_http_clear_accept_ranges(r); ngx_http_clear_last_modified(r); ngx_http_clear_etag(r); rc = ngx_http_send_header(r); if (rc == NGX_ERROR || r->header_only) { return rc; } b = ngx_create_temp_buf(r->pool, size); if (b == NULL) { return NGX_ERROR; } p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head, sizeof(ngx_http_msie_refresh_head) - 1); if (escape == 0) { p = ngx_cpymem(p, location, len); } else { p = (u_char *) ngx_escape_uri(p, location, len, NGX_ESCAPE_REFRESH); } b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail, sizeof(ngx_http_msie_refresh_tail) - 1); b->last_buf = 1; b->last_in_chain = 1; out.buf = b; out.next = NULL; return ngx_http_output_filter(r, &out); }
static ngx_int_t ngx_http_gzip_header_filter(ngx_http_request_t *r) { ngx_table_elt_t *h; ngx_http_gzip_ctx_t *ctx; ngx_http_gzip_conf_t *conf; conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); if (!conf->enable || (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_FORBIDDEN && r->headers_out.status != NGX_HTTP_NOT_FOUND) || (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) || (r->headers_out.content_length_n != -1 && r->headers_out.content_length_n < conf->min_length) || ngx_http_test_content_type(r, &conf->types) == NULL || r->header_only) { return ngx_http_next_header_filter(r); } r->gzip_vary = 1; #if (NGX_HTTP_DEGRADATION) { ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (clcf->gzip_disable_degradation && ngx_http_degraded(r)) { return ngx_http_next_header_filter(r); } } #endif if (!r->gzip_tested) { if (ngx_http_gzip_ok(r) != NGX_OK) { return ngx_http_next_header_filter(r); } } else if (!r->gzip_ok) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module); ctx->request = r; ctx->buffering = (conf->postpone_gzipping != 0); ngx_http_gzip_filter_memory(r, ctx); h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NGX_ERROR; } h->hash = 1; ngx_str_set(&h->key, "Content-Encoding"); ngx_str_set(&h->value, "gzip"); r->headers_out.content_encoding = h; r->main_filter_need_in_memory = 1; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); ngx_http_weak_etag(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_brotli_header_filter(ngx_http_request_t *r) { ngx_table_elt_t *h; ngx_http_brotli_ctx_t *ctx; ngx_http_brotli_conf_t *conf; conf = ngx_http_get_module_loc_conf(r, ngx_http_brotli_filter_module); if (!conf->enable || (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_FORBIDDEN && r->headers_out.status != NGX_HTTP_NOT_FOUND) || (r->headers_out.content_length_n != -1 && r->headers_out.content_length_n < conf->min_length) || ngx_http_test_content_type(r, &conf->types) == NULL || r->header_only) { return ngx_http_next_header_filter(r); } if (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) { return ngx_http_next_header_filter(r); } /* Check that brotli is supported. We do not check possible q value * if brotli is supported it takes precendence over gzip if size > * brotli_min_length */ if (accept_br(r->headers_in.accept_encoding) != NGX_OK) { return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_brotli_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } #if (NGX_HTTP_GZIP) r->gzip_vary = 1; /* Make sure gzip does not execute */ r->gzip_tested = 1; r->gzip_ok = 0; #endif ngx_http_set_ctx(r, ctx, ngx_http_brotli_filter_module); ctx->request = r; h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NGX_ERROR; } h->hash = 1; ngx_str_set(&h->key, "Content-Encoding"); ngx_str_set(&h->value, "br"); r->headers_out.content_encoding = h; r->main_filter_need_in_memory = 1; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); ngx_http_weak_etag(r); return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_xss_header_filter(ngx_http_request_t *r) { ngx_http_xss_ctx_t *ctx; ngx_http_xss_loc_conf_t *xlcf; ngx_str_t callback; u_char *p, *src, *dst; if (r != r->main) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped in subrequests"); return ngx_http_next_header_filter(r); } xlcf = ngx_http_get_module_loc_conf(r, ngx_http_xss_filter_module); if (!xlcf->get_enabled) { return ngx_http_next_header_filter(r); } if (r->method != NGX_HTTP_GET) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped due to the unmatched request method: %V", &r->method_name); return ngx_http_next_header_filter(r); } if (xlcf->check_status) { if (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_CREATED) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped due to unmatched response status " "\"%ui\"", r->headers_out.status); return ngx_http_next_header_filter(r); } } if (xlcf->callback_arg.len == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: xss_get is enabled but no xss_callback_arg " "specified"); return ngx_http_next_header_filter(r); } if (ngx_http_test_content_type(r, &xlcf->input_types) == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped due to unmatched Content-Type response " "header"); return ngx_http_next_header_filter(r); } if (ngx_http_arg(r, xlcf->callback_arg.data, xlcf->callback_arg.len, &callback) != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped: no GET argument \"%V\" specified in " "the request", &xlcf->callback_arg); return ngx_http_next_header_filter(r); } p = ngx_palloc(r->pool, callback.len); if (p == NULL) { return NGX_ERROR; } src = callback.data; dst = p; ngx_unescape_uri(&dst, &src, callback.len, NGX_UNESCAPE_URI_COMPONENT); if (src != callback.data + callback.len) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: unescape uri: input data not consumed completely"); return NGX_ERROR; } callback.data = p; callback.len = dst - p; if (ngx_http_xss_test_callback(callback.data, callback.len) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: bad callback argument: \"%V\"", &callback); return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xss_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } /* * set by ngx_pcalloc(): * * ctx->callback = { 0, NULL }; * ctx->before_body_sent = 0; */ ctx->callback = callback; ngx_http_set_ctx(r, ctx, ngx_http_xss_filter_module); r->headers_out.content_type = xlcf->output_type; r->headers_out.content_type_len = xlcf->output_type.len; r->headers_out.content_type_lowcase = NULL; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss output Content-Type header \"%V\"", &xlcf->output_type); ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); if (xlcf->override_status && r->headers_out.status >= NGX_HTTP_SPECIAL_RESPONSE) { r->headers_out.status = NGX_HTTP_OK; } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_dynamic_etags_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_chain_t *chain_link; ngx_http_dynamic_etags_module_ctx_t *ctx; ngx_int_t rc; ngx_md5_t md5; unsigned char digest[16]; ngx_uint_t i; ctx = ngx_http_get_module_ctx(r, ngx_http_dynamic_etags_module); if (ctx == NULL) { return ngx_http_next_body_filter(r, in); } ngx_http_dynamic_etags_loc_conf_t *loc_conf; loc_conf = ngx_http_get_module_loc_conf(r, ngx_http_dynamic_etags_module); if (1 == loc_conf->enable) { ngx_md5_init(&md5); for (chain_link = in; chain_link; chain_link = chain_link->next) { ngx_md5_update(&md5, chain_link->buf->pos, chain_link->buf->last - chain_link->buf->pos); } ngx_md5_final(digest, &md5); unsigned char* etag = ngx_pcalloc(r->pool, 34); etag[0] = etag[33] = '"'; for ( i = 0 ; i < 16; i++ ) { etag[2*i+1] = hex[digest[i] >> 4]; etag[2*i+2] = hex[digest[i] & 0xf]; } if(!r->headers_out.etag) { r->headers_out.etag = ngx_list_push(&r->headers_out.headers); } r->headers_out.etag->hash = 1; r->headers_out.etag->key.len = sizeof("ETag") - 1; r->headers_out.etag->key.data = (u_char *) "ETag"; r->headers_out.etag->value.len = 34; r->headers_out.etag->value.data = etag; /* look for If-None-Match in request headers */ ngx_uint_t found=0; ngx_list_part_t *part = NULL; ngx_table_elt_t *header = NULL; ngx_table_elt_t if_none_match; part = &r->headers_in.headers.part; header = part->elts; for ( i = 0 ; ; i++ ) { if ( i >= part->nelts) { if ( part->next == NULL ) { break; } part = part->next; header = part->elts; i = 0; } if ( ngx_strcmp(header[i].key.data, "If-None-Match") == 0 ) { if_none_match = header[i]; found = 1; break; } } if ( found ) { if ( ngx_strncmp(r->headers_out.etag->value.data, if_none_match.value.data, r->headers_out.etag->value.len) == 0 ) { r->headers_out.status = NGX_HTTP_NOT_MODIFIED; r->headers_out.status_line.len = 0; r->headers_out.content_type.len = 0; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); } } }
static ngx_int_t ngx_http_send_special_response(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf, ngx_uint_t err) { u_char *tail; size_t len; ngx_int_t rc; ngx_buf_t *b; ngx_uint_t msie_padding; ngx_chain_t out[3]; if (clcf->server_tokens) { len = sizeof(ngx_http_error_full_tail) - 1; tail = ngx_http_error_full_tail; } else { len = sizeof(ngx_http_error_tail) - 1; tail = ngx_http_error_tail; } msie_padding = 0; if (ngx_http_error_pages[err].len) { r->headers_out.content_length_n = ngx_http_error_pages[err].len + len; if (clcf->msie_padding && (r->headers_in.msie || r->headers_in.chrome) && r->http_version >= NGX_HTTP_VERSION_10 && err >= NGX_HTTP_OFF_4XX) { r->headers_out.content_length_n += sizeof(ngx_http_msie_padding) - 1; msie_padding = 1; } r->headers_out.content_type_len = sizeof("text/html") - 1; ngx_str_set(&r->headers_out.content_type, "text/html"); r->headers_out.content_type_lowcase = NULL; } else { r->headers_out.content_length_n = 0; } if (r->headers_out.content_length) { r->headers_out.content_length->hash = 0; r->headers_out.content_length = NULL; } ngx_http_clear_accept_ranges(r); ngx_http_clear_last_modified(r); ngx_http_clear_etag(r); rc = ngx_http_send_header(r); if (rc == NGX_ERROR || r->header_only) { return rc; } if (ngx_http_error_pages[err].len == 0) { return ngx_http_send_special(r, NGX_HTTP_LAST); } b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } b->memory = 1; b->pos = ngx_http_error_pages[err].data; b->last = ngx_http_error_pages[err].data + ngx_http_error_pages[err].len; out[0].buf = b; out[0].next = &out[1]; b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } b->memory = 1; b->pos = tail; b->last = tail + len; out[1].buf = b; out[1].next = NULL; if (msie_padding) { b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } b->memory = 1; b->pos = ngx_http_msie_padding; b->last = ngx_http_msie_padding + sizeof(ngx_http_msie_padding) - 1; out[1].next = &out[2]; out[2].buf = b; out[2].next = NULL; } if (r == r->main) { b->last_buf = 1; } b->last_in_chain = 1; return ngx_http_output_filter(r, &out[0]); }
static ngx_int_t ngx_http_zip_set_headers(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx) { time_t if_range, last_modified; if (ngx_http_zip_add_cache_control(r) == NGX_ERROR) { return NGX_ERROR; } r->headers_out.content_type_len = sizeof(NGX_ZIP_MIME_TYPE) - 1; ngx_str_set(&r->headers_out.content_type, NGX_ZIP_MIME_TYPE); ngx_http_clear_content_length(r); if (ctx->missing_crc32) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: Clearing Accept-Ranges header"); ngx_http_clear_accept_ranges(r); } r->headers_out.content_length_n = ctx->archive_size; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: Archive will be %O bytes", ctx->archive_size); if (r->headers_in.range) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: Range found"); if (ctx->missing_crc32) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: Missing checksums, ignoring Range"); return NGX_OK; } if (r->headers_in.if_range && r->upstream) { if_range = ngx_http_parse_time(r->headers_in.if_range->value.data, r->headers_in.if_range->value.len); if (if_range == NGX_ERROR) { /* treat as ETag */ if (r->upstream->headers_in.etag) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: If-Range = %V, ETag = %V", &r->headers_in.if_range->value, &r->upstream->headers_in.etag->value); if (r->upstream->headers_in.etag->value.len != r->headers_in.if_range->value.len || ngx_strncmp(r->upstream->headers_in.etag->value.data, r->headers_in.if_range->value.data, r->headers_in.if_range->value.len)) { return NGX_OK; } } else { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: No ETag from upstream"); return NGX_OK; } } else { /* treat as modification time */ if (r->upstream->headers_in.last_modified) { last_modified = ngx_http_parse_time(r->upstream->headers_in.last_modified->value.data, r->upstream->headers_in.last_modified->value.len); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: If-Range = %d, Last-Modified = %d", if_range, last_modified); if (if_range != last_modified && last_modified != -1) { return NGX_OK; } } else { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: No Last-Modified from upstream"); return NGX_OK; } } } if (ngx_http_zip_parse_range(r, &r->headers_in.range->value, ctx) == NGX_ERROR) { r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE; if (ngx_http_zip_add_full_content_range(r) == NGX_ERROR) { return NGX_ERROR; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: Range not satisfiable"); ctx->ranges.nelts = 0; return NGX_HTTP_RANGE_NOT_SATISFIABLE; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mod_zip: Range is satisfiable"); if (ctx->ranges.nelts == 1) { if (ngx_http_zip_add_partial_content_range(r, ctx) == NGX_ERROR) { return NGX_ERROR; } } else { if (ngx_http_zip_init_multipart_range(r, ctx) == NGX_ERROR) { return NGX_ERROR; } } r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; r->headers_out.status_line.len = 0; } return NGX_OK; }