diff options
author | kaiwu <kaiwu2004@gmail.com> | 2025-03-01 12:42:23 +0800 |
---|---|---|
committer | kaiwu <kaiwu2004@gmail.com> | 2025-03-01 12:42:23 +0800 |
commit | 3f33461e4948bf05e60bdff35ec6c57a649c7860 (patch) | |
tree | 284c2ba95a41536ae1bff6bea710db0709a64739 /srcache-nginx-module-0.33/src/ngx_http_srcache_util.c | |
download | openresty-3f33461e4948bf05e60bdff35ec6c57a649c7860.tar.gz openresty-3f33461e4948bf05e60bdff35ec6c57a649c7860.zip |
openresty bundle
Diffstat (limited to 'srcache-nginx-module-0.33/src/ngx_http_srcache_util.c')
-rw-r--r-- | srcache-nginx-module-0.33/src/ngx_http_srcache_util.c | 1296 |
1 files changed, 1296 insertions, 0 deletions
diff --git a/srcache-nginx-module-0.33/src/ngx_http_srcache_util.c b/srcache-nginx-module-0.33/src/ngx_http_srcache_util.c new file mode 100644 index 0000000..bfa1eaa --- /dev/null +++ b/srcache-nginx-module-0.33/src/ngx_http_srcache_util.c @@ -0,0 +1,1296 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_http_srcache_util.h" +#include "ngx_http_srcache_headers.h" + + +static ngx_int_t ngx_http_srcache_set_content_length_header( + ngx_http_request_t *r, off_t len); + + +static ngx_str_t ngx_http_status_lines[] = { + + ngx_string("200 OK"), + ngx_string("201 Created"), + ngx_string("202 Accepted"), + ngx_null_string, /* "203 Non-Authoritative Information" */ + ngx_string("204 No Content"), + ngx_null_string, /* "205 Reset Content" */ + ngx_string("206 Partial Content"), + + /* ngx_null_string, */ /* "207 Multi-Status" */ + +#define NGX_HTTP_LAST_LEVEL_200 207 +#define NGX_HTTP_LEVEL_200 (NGX_HTTP_LAST_LEVEL_200 - 200) + + /* ngx_null_string, */ /* "300 Multiple Choices" */ + + ngx_string("301 Moved Permanently"), + ngx_string("302 Moved Temporarily"), + ngx_string("303 See Other"), + ngx_string("304 Not Modified"), + ngx_null_string, /* "305 Use Proxy" */ + ngx_null_string, /* "306 unused" */ + ngx_string("307 Temporary Redirect"), + ngx_string("308 Permanent Redirect"), + +#define NGX_HTTP_LAST_LEVEL_300 309 +#define NGX_HTTP_LEVEL_300 (NGX_HTTP_LAST_LEVEL_300 - 301) + + ngx_string("400 Bad Request"), + ngx_string("401 Unauthorized"), + ngx_string("402 Payment Required"), + ngx_string("403 Forbidden"), + ngx_string("404 Not Found"), + ngx_string("405 Not Allowed"), + ngx_string("406 Not Acceptable"), + ngx_null_string, /* "407 Proxy Authentication Required" */ + ngx_string("408 Request Time-out"), + ngx_string("409 Conflict"), + ngx_string("410 Gone"), + ngx_string("411 Length Required"), + ngx_string("412 Precondition Failed"), + ngx_string("413 Request Entity Too Large"), + ngx_null_string, /* "414 Request-URI Too Large", but we never send it + * because we treat such requests as the HTTP/0.9 + * requests and send only a body without a header + */ + ngx_string("415 Unsupported Media Type"), + ngx_string("416 Requested Range Not Satisfiable"), + + /* ngx_null_string, */ /* "417 Expectation Failed" */ + /* ngx_null_string, */ /* "418 unused" */ + /* ngx_null_string, */ /* "419 unused" */ + /* ngx_null_string, */ /* "420 unused" */ + /* ngx_null_string, */ /* "421 unused" */ + /* ngx_null_string, */ /* "422 Unprocessable Entity" */ + /* ngx_null_string, */ /* "423 Locked" */ + /* ngx_null_string, */ /* "424 Failed Dependency" */ + +#define NGX_HTTP_LAST_LEVEL_400 417 +#define NGX_HTTP_LEVEL_400 (NGX_HTTP_LAST_LEVEL_400 - 400) + + ngx_string("500 Internal Server Error"), + ngx_string("501 Method Not Implemented"), + ngx_string("502 Bad Gateway"), + ngx_string("503 Service Temporarily Unavailable"), + ngx_string("504 Gateway Time-out"), + + ngx_null_string, /* "505 HTTP Version Not Supported" */ + ngx_null_string, /* "506 Variant Also Negotiates" */ + ngx_string("507 Insufficient Storage"), + /* ngx_null_string, */ /* "508 unused" */ + /* ngx_null_string, */ /* "509 unused" */ + /* ngx_null_string, */ /* "510 Not Extended" */ + +#define NGX_HTTP_LAST_LEVEL_500 508 + +}; + + +ngx_str_t ngx_http_srcache_content_length_header_key = + ngx_string("Content-Length"); +ngx_str_t ngx_http_srcache_get_method = + ngx_http_srcache_method_name("GET"); +ngx_str_t ngx_http_srcache_put_method = + ngx_http_srcache_method_name("PUT"); +ngx_str_t ngx_http_srcache_post_method = + ngx_http_srcache_method_name("POST"); +ngx_str_t ngx_http_srcache_head_method = + ngx_http_srcache_method_name("HEAD"); +ngx_str_t ngx_http_srcache_copy_method = + ngx_http_srcache_method_name("COPY"); +ngx_str_t ngx_http_srcache_move_method = + ngx_http_srcache_method_name("MOVE"); +ngx_str_t ngx_http_srcache_lock_method = + ngx_http_srcache_method_name("LOCK"); +ngx_str_t ngx_http_srcache_mkcol_method = + ngx_http_srcache_method_name("MKCOL"); +ngx_str_t ngx_http_srcache_trace_method = + ngx_http_srcache_method_name("TRACE"); +ngx_str_t ngx_http_srcache_delete_method = + ngx_http_srcache_method_name("DELETE"); +ngx_str_t ngx_http_srcache_unlock_method = + ngx_http_srcache_method_name("UNLOCK"); +ngx_str_t ngx_http_srcache_options_method = + ngx_http_srcache_method_name("OPTIONS"); +ngx_str_t ngx_http_srcache_propfind_method = + ngx_http_srcache_method_name("PROPFIND"); +ngx_str_t ngx_http_srcache_proppatch_method = + ngx_http_srcache_method_name("PROPPATCH"); + + +void +ngx_http_srcache_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in) +{ + ngx_chain_t *cl; + + for (cl = in; cl; cl = cl->next) { + cl->buf->pos = cl->buf->last; + } +} + + +ngx_int_t +ngx_http_srcache_parse_method_name(ngx_str_t **method_name_ptr) +{ + const ngx_str_t *method_name = *method_name_ptr; + + switch (method_name->len) { + + case 3: + if (ngx_http_srcache_strcmp_const(method_name->data, "GET") == 0) { + *method_name_ptr = &ngx_http_srcache_get_method; + return NGX_HTTP_GET; + } + + if (ngx_http_srcache_strcmp_const(method_name->data, "PUT") == 0) { + *method_name_ptr = &ngx_http_srcache_put_method; + return NGX_HTTP_PUT; + } + + return NGX_HTTP_UNKNOWN; + + case 4: + if (ngx_http_srcache_strcmp_const(method_name->data, "POST") == 0) { + *method_name_ptr = &ngx_http_srcache_post_method; + return NGX_HTTP_POST; + } + + if (ngx_http_srcache_strcmp_const(method_name->data, "HEAD") == 0) { + *method_name_ptr = &ngx_http_srcache_head_method; + return NGX_HTTP_HEAD; + } + + if (ngx_http_srcache_strcmp_const(method_name->data, "COPY") == 0) { + *method_name_ptr = &ngx_http_srcache_copy_method; + return NGX_HTTP_COPY; + } + + if (ngx_http_srcache_strcmp_const(method_name->data, "MOVE") == 0) { + *method_name_ptr = &ngx_http_srcache_move_method; + return NGX_HTTP_MOVE; + } + + if (ngx_http_srcache_strcmp_const(method_name->data, "LOCK") == 0) { + *method_name_ptr = &ngx_http_srcache_lock_method; + return NGX_HTTP_LOCK; + } + + return NGX_HTTP_UNKNOWN; + + case 5: + if (ngx_http_srcache_strcmp_const(method_name->data, "MKCOL") == 0) { + *method_name_ptr = &ngx_http_srcache_mkcol_method; + return NGX_HTTP_MKCOL; + } + + if (ngx_http_srcache_strcmp_const(method_name->data, "TRACE") == 0) { + *method_name_ptr = &ngx_http_srcache_trace_method; + return NGX_HTTP_TRACE; + } + + return NGX_HTTP_UNKNOWN; + + case 6: + if (ngx_http_srcache_strcmp_const(method_name->data, "DELETE") == 0) { + *method_name_ptr = &ngx_http_srcache_delete_method; + return NGX_HTTP_DELETE; + } + + if (ngx_http_srcache_strcmp_const(method_name->data, "UNLOCK") == 0) { + *method_name_ptr = &ngx_http_srcache_unlock_method; + return NGX_HTTP_UNLOCK; + } + + return NGX_HTTP_UNKNOWN; + + case 7: + if (ngx_http_srcache_strcmp_const(method_name->data, "OPTIONS") == 0) { + *method_name_ptr = &ngx_http_srcache_options_method; + return NGX_HTTP_OPTIONS; + } + + return NGX_HTTP_UNKNOWN; + + case 8: + if (ngx_http_srcache_strcmp_const(method_name->data, "PROPFIND") == 0) { + *method_name_ptr = &ngx_http_srcache_propfind_method; + return NGX_HTTP_PROPFIND; + } + + return NGX_HTTP_UNKNOWN; + + case 9: + if (ngx_http_srcache_strcmp_const(method_name->data, "PROPPATCH") + == 0) + { + *method_name_ptr = &ngx_http_srcache_proppatch_method; + return NGX_HTTP_PROPPATCH; + } + + return NGX_HTTP_UNKNOWN; + + default: + return NGX_HTTP_UNKNOWN; + } + + return NGX_HTTP_UNKNOWN; +} + + +ngx_int_t +ngx_http_srcache_adjust_subrequest(ngx_http_request_t *sr, + ngx_http_srcache_parsed_request_t *parsed_sr) +{ + ngx_http_core_main_conf_t *cmcf; + ngx_http_request_t *r; + ngx_http_request_body_t *body; + ngx_int_t rc; + + sr->method = parsed_sr->method; + sr->method_name = parsed_sr->method_name; + + r = sr->parent; + + dd("subrequest method: %d %.*s", (int) sr->method, + (int) sr->method_name.len, sr->method_name.data); + + sr->header_in = r->header_in; + +#if 1 + /* XXX work-around a bug in ngx_http_subrequest */ + if (r->headers_in.headers.last == &r->headers_in.headers.part) { + sr->headers_in.headers.last = &sr->headers_in.headers.part; + } +#endif + + /* we do not inherit the parent request's variables */ + cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module); + sr->variables = ngx_pcalloc(sr->pool, cmcf->variables.nelts + * sizeof(ngx_http_variable_value_t)); + + if (sr->variables == NULL) { + return NGX_ERROR; + } + + body = parsed_sr->request_body; + if (body) { + sr->request_body = body; + + rc = ngx_http_srcache_set_content_length_header(sr, + parsed_sr->content_length_n); + + if (rc != NGX_OK) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_srcache_add_copy_chain(ngx_pool_t *pool, ngx_chain_t **chain, + ngx_chain_t *in, unsigned *plast) +{ + ngx_chain_t *cl, **ll; + size_t len; + + ll = chain; + + for (cl = *chain; cl; cl = cl->next) { + ll = &cl->next; + } + + *plast = 0; + + while (in) { + cl = ngx_alloc_chain_link(pool); + if (cl == NULL) { + return NGX_ERROR; + } + + if (in->buf->last_buf || in->buf->last_in_chain) { + *plast = 1; + } + + if (ngx_buf_special(in->buf)) { + cl->buf = in->buf; + + } else { + if (ngx_buf_in_memory(in->buf)) { + len = ngx_buf_size(in->buf); + cl->buf = ngx_create_temp_buf(pool, len); + if (cl->buf == NULL) { + return NGX_ERROR; + } + + dd("buf: %.*s", (int) len, in->buf->pos); + + cl->buf->last = ngx_copy(cl->buf->pos, in->buf->pos, len); + + } else { + return NGX_ERROR; + } + } + + *ll = cl; + ll = &cl->next; + in = in->next; + } + + *ll = NULL; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_srcache_post_request_at_head(ngx_http_request_t *r, + ngx_http_posted_request_t *pr) +{ + dd_enter(); + + if (pr == NULL) { + pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t)); + if (pr == NULL) { + return NGX_ERROR; + } + } + + pr->request = r; + pr->next = r->main->posted_requests; + r->main->posted_requests = pr; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_srcache_set_content_length_header(ngx_http_request_t *r, off_t len) +{ + ngx_table_elt_t *h, *header; + u_char *p; + ngx_list_part_t *part; + ngx_http_request_t *pr; + ngx_uint_t i; + + r->headers_in.content_length_n = len; + + if (ngx_list_init(&r->headers_in.headers, r->pool, 20, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + return NGX_ERROR; + } + + h->key = ngx_http_srcache_content_length_header_key; + h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); + if (h->lowcase_key == NULL) { + return NGX_ERROR; + } + + ngx_strlow(h->lowcase_key, h->key.data, h->key.len); + + r->headers_in.content_length = h; + + p = ngx_palloc(r->pool, NGX_OFF_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + h->value.data = p; + + h->value.len = ngx_sprintf(h->value.data, "%O", len) - h->value.data; + + h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( + ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash( + ngx_hash('c', 'o'), 'n'), 't'), 'e'), 'n'), 't'), '-'), 'l'), 'e'), + 'n'), 'g'), 't'), 'h'); + + dd("r content length: %.*s", + (int) r->headers_in.content_length->value.len, + r->headers_in.content_length->value.data); + + pr = r->parent; + + if (pr == NULL) { + return NGX_OK; + } + + /* forward the parent request's all other request headers */ + + part = &pr->headers_in.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].key.len == sizeof("Content-Length") - 1 + && ngx_strncasecmp(header[i].key.data, + (u_char *) "Content-Length", + sizeof("Content-Length") - 1) == 0) + { + continue; + } + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = header[i]; + } + + /* XXX maybe we should set those built-in header slot in + * ngx_http_headers_in_t too? */ + + return NGX_OK; +} + + +ngx_int_t +ngx_http_srcache_request_no_cache(ngx_http_request_t *r, unsigned *no_store) +{ + ngx_table_elt_t *h; + ngx_list_part_t *part; + u_char *p; + u_char *last; + ngx_uint_t i; + unsigned no_cache; + + part = &r->headers_in.headers.part; + h = part->elts; + + *no_store = 0; + no_cache = 0; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + if (h[i].key.len == sizeof("Cache-Control") - 1 + && ngx_strncasecmp(h[i].key.data, (u_char *) "Cache-Control", + sizeof("Cache-Control") - 1) == 0) + { + p = h[i].value.data; + last = p + h[i].value.len; + + if (!*no_store + && ngx_strlcasestrn(p, last, (u_char *) "no-store", 8 - 1) + != NULL) + { + *no_store = 1; + } + + if (ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL) + { + no_cache = 1; + } + + continue; + } + + if (h[i].key.len == sizeof("Pragma") - 1 + && ngx_strncasecmp(h[i].key.data, (u_char *) "Pragma", + sizeof("Pragma") - 1) == 0) + { + p = h[i].value.data; + last = p + h[i].value.len; + + if (ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL) + { + no_cache = 1; + } + } + } + + return no_cache ? NGX_OK : NGX_DECLINED; +} + + +ngx_int_t +ngx_http_srcache_response_no_cache(ngx_http_request_t *r, + ngx_http_srcache_loc_conf_t *conf, ngx_http_srcache_ctx_t *ctx) +{ + ngx_table_elt_t *h; +#if defined(nginx_version) && nginx_version >= 1023000 + ngx_table_elt_t *cc; +#else + ngx_table_elt_t **ccp; + ngx_uint_t i; +#endif + u_char *p, *last; + ngx_int_t n; + time_t expires; + + dd("checking response cache control settings"); + +#if defined(nginx_version) && nginx_version >= 1023000 + cc = r->headers_out.cache_control; + + if (cc == NULL) { + goto check_expires; + } + + for (; cc; cc = cc->next) { + if (!cc->hash) { + continue; + } + + p = cc->value.data; + last = p + cc->value.len; +#else + ccp = r->headers_out.cache_control.elts; + + if (ccp == NULL) { + goto check_expires; + } + + for (i = 0; i < r->headers_out.cache_control.nelts; i++) { + if (!ccp[i]->hash) { + continue; + } + + p = ccp[i]->value.data; + last = p + ccp[i]->value.len; +#endif + + if (!conf->store_private + && ngx_strlcasestrn(p, last, (u_char *) "private", 7 - 1) != NULL) + { + return NGX_OK; + } + + if (!conf->store_no_store + && ngx_strlcasestrn(p, last, (u_char *) "no-store", 8 - 1) != NULL) + { + return NGX_OK; + } + + if (!conf->store_no_cache + && ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL) + { + return NGX_OK; + } + + if (ctx->valid_sec != 0) { + continue; + } + + p = ngx_strlcasestrn(p, last, (u_char *) "max-age=", 8 - 1); + + if (p == NULL) { + continue; + } + + n = 0; + + for (p += 8; p < last; p++) { + if (*p == ',' || *p == ';' || *p == ' ') { + break; + } + + if (*p >= '0' && *p <= '9') { + n = n * 10 + *p - '0'; + continue; + } + + return NGX_OK; + } + + if (n == 0) { + return NGX_OK; + } + + ctx->valid_sec = ngx_time() + n; + } + +check_expires: + + dd("valid_sec after processing cache-control: %d", (int) ctx->valid_sec); + + if (ctx->valid_sec == 0) { + h = r->headers_out.expires; + + dd("expires header: %p", h); + + if (h != NULL && h->hash != 0) { + expires = ngx_http_parse_time(h->value.data, h->value.len); + + if (expires == NGX_ERROR || expires <= ngx_time()) { + return NGX_OK; + } + + ctx->valid_sec = expires; + } + } + + return NGX_DECLINED; +} + + +ngx_int_t +ngx_http_srcache_process_status_line(ngx_http_request_t *r, ngx_buf_t *b) +{ + ngx_int_t rc; + ngx_http_srcache_ctx_t *ctx; + ngx_http_request_t *pr; + ngx_http_srcache_loc_conf_t *conf; + + ctx = ngx_http_get_module_ctx(r, ngx_http_srcache_filter_module); + + rc = ngx_http_parse_status_line(r, b, &ctx->status); + + if (rc == NGX_AGAIN) { + return NGX_AGAIN; + } + + if (rc == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "srcache_fetch: cache sent invalid status line"); + return NGX_ERROR; + } + + /* rc == NGX_OK */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "srcache_fetch status line done"); + + pr = r->parent; + pr->headers_out.status = ctx->status.code; + + ctx->process_header = ngx_http_srcache_process_header; + + conf = ngx_http_get_module_loc_conf(pr, ngx_http_srcache_filter_module); + + dd("header buffer size: %d", (int) conf->header_buf_size); + + ctx->header_buf = ngx_create_temp_buf(r->pool, conf->header_buf_size); + if (ctx->header_buf == NULL) { + return NGX_ERROR; + } + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + return ngx_http_srcache_process_header(r, b); +} + + +ngx_int_t +ngx_http_srcache_process_header(ngx_http_request_t *r, ngx_buf_t *b) +{ + ngx_int_t rc; + ngx_table_elt_t header; + ngx_http_srcache_ctx_t *ctx; + off_t len, rest; + unsigned truncate; + u_char *p; + ngx_http_srcache_header_t *hh; + ngx_http_srcache_main_conf_t *smcf; + + smcf = ngx_http_get_module_main_conf(r, ngx_http_srcache_filter_module); + + ctx = ngx_http_get_module_ctx(r, ngx_http_srcache_filter_module); + + for ( ;; ) { + + len = b->last - b->pos; + rest = ctx->header_buf->end - ctx->header_buf->last; + + dd("len: %d, rest: %d", (int) len, (int) rest); + + if (len > rest) { + len = rest; + truncate = 1; + + } else { + truncate = 0; + } + + ctx->header_buf->last = ngx_copy(ctx->header_buf->last, b->pos, + (size_t) len); + + p = ctx->header_buf->pos; + + rc = ngx_http_parse_header_line(r, ctx->header_buf, 1); + + b->pos += ctx->header_buf->pos - p; + + if (rc == NGX_OK) { + + /* a header line has been parsed successfully */ + + ngx_memzero(&header, sizeof(ngx_table_elt_t)); + + header.hash = r->header_hash; + + header.key.len = r->header_name_end - r->header_name_start; + header.value.len = r->header_end - r->header_start; + + header.key.data = ngx_pnalloc(r->pool, + header.key.len + 1 + + header.value.len + 1 + + header.key.len); + + if (header.key.data == NULL) { + return NGX_ERROR; + } + + header.value.data = header.key.data + header.key.len + 1; + header.lowcase_key = header.key.data + header.key.len + 1 + + header.value.len + 1; + + ngx_cpystrn(header.key.data, r->header_name_start, + header.key.len + 1); + + ngx_cpystrn(header.value.data, r->header_start, + header.value.len + 1); + + if (header.key.len == r->lowcase_index) { + ngx_memcpy(header.lowcase_key, r->lowcase_header, + header.key.len); + + } else { + ngx_strlow(header.lowcase_key, header.key.data, header.key.len); + } + + hh = ngx_hash_find(&smcf->headers_in_hash, header.hash, + header.lowcase_key, header.key.len); + + if (hh) { + if (hh->handler(r->parent, &header, hh->offset) != NGX_OK) { + return NGX_ERROR; + } + + } else { + + if (ngx_http_srcache_process_header_line(r->parent, &header, 0) + != NGX_OK) + { + return NGX_ERROR; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "srcache_fetch header: \"%V: %V\"", + &header.key, &header.value); + + ctx->header_buf->pos = ctx->header_buf->start; + ctx->header_buf->last = ctx->header_buf->start; + + continue; + } + + if (rc == NGX_HTTP_PARSE_HEADER_DONE) { + + /* a whole header has been parsed successfully */ + + ctx->header_buf->pos = ctx->header_buf->start; + ctx->header_buf->last = ctx->header_buf->start; + ngx_pfree(r->pool, ctx->header_buf->start); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "srcache_fetch header done"); + + return NGX_OK; + } + + if (rc == NGX_AGAIN) { + if (truncate) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "srcache_fetch: header buffer overflown " + "(maybe you should consider increasing " + "srcache_header_buffer_size?)"); + + ctx->header_buf->pos = ctx->header_buf->start; + ctx->header_buf->last = ctx->header_buf->start; + ngx_pfree(r->pool, ctx->header_buf->start); + + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + /* there was error while a header line parsing */ + + ctx->header_buf->pos = ctx->header_buf->start; + ctx->header_buf->last = ctx->header_buf->start; + ngx_pfree(r->pool, ctx->header_buf->start); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "srcache_fetch: cache sent invalid header"); + + return NGX_ERROR; + } +} + + +ngx_int_t +ngx_http_srcache_store_response_header(ngx_http_request_t *r, + ngx_http_srcache_ctx_t *ctx) +{ + ngx_chain_t *cl; + size_t len; + ngx_buf_t *b; + ngx_uint_t status; + ngx_uint_t i; + ngx_str_t *status_line; + ngx_list_part_t *part; + ngx_table_elt_t *header; + + u_char buf[sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1]; + + ngx_http_srcache_loc_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_srcache_filter_module); + + dd("request: %p, uri: %.*s", r, (int) r->uri.len, r->uri.data); + + len = sizeof("HTTP/1.x ") - 1 + sizeof(CRLF) - 1 + /* the end of the header */ + + sizeof(CRLF) - 1; + + if (r->headers_out.status_line.len) { + dd("status line defined"); + len += r->headers_out.status_line.len; + status_line = &r->headers_out.status_line; + status = 0; + + } else { + dd("status line not defined"); + + status = r->headers_out.status; + + if (status >= NGX_HTTP_OK + && status < NGX_HTTP_LAST_LEVEL_200) + { + /* 2XX */ + + status -= NGX_HTTP_OK; + dd("status: %d", (int) status); + status_line = &ngx_http_status_lines[status]; + len += ngx_http_status_lines[status].len; + + } else if (status >= NGX_HTTP_MOVED_PERMANENTLY + && status < NGX_HTTP_LAST_LEVEL_300) + { + /* 3XX */ + + if (status == NGX_HTTP_NOT_MODIFIED) { + r->header_only = 1; + } + + status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_LEVEL_200; + status_line = &ngx_http_status_lines[status]; + len += ngx_http_status_lines[status].len; + + } else if (status >= NGX_HTTP_BAD_REQUEST + && status < NGX_HTTP_LAST_LEVEL_400) + { + /* 4XX */ + status = status - NGX_HTTP_BAD_REQUEST + + NGX_HTTP_LEVEL_200 + + NGX_HTTP_LEVEL_300; + + status_line = &ngx_http_status_lines[status]; + len += ngx_http_status_lines[status].len; + + } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR + && status < NGX_HTTP_LAST_LEVEL_500) + { + /* 5XX */ + status = status - NGX_HTTP_INTERNAL_SERVER_ERROR + + NGX_HTTP_LEVEL_200 + + NGX_HTTP_LEVEL_300 + + NGX_HTTP_LEVEL_400; + + status_line = &ngx_http_status_lines[status]; + len += ngx_http_status_lines[status].len; + + } else { + len += NGX_INT_T_LEN; + status_line = NULL; + } + } + + if (!conf->hide_content_type && r->headers_out.content_type.len) { + len += sizeof("Content-Type: ") - 1 + + r->headers_out.content_type.len + 2; + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + len += sizeof("; charset=") - 1 + r->headers_out.charset.len; + } + } + + if (!conf->hide_last_modified) { + if (r->headers_out.last_modified_time != -1) { + if (r->headers_out.status != NGX_HTTP_OK + && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT + && r->headers_out.status != NGX_HTTP_NOT_MODIFIED + && r->headers_out.status != NGX_HTTP_NO_CONTENT) + { + r->headers_out.last_modified_time = -1; + r->headers_out.last_modified = NULL; + } + } + + dd("last modified time: %d", (int) r->headers_out.last_modified_time); + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + (void) ngx_http_time(buf, r->headers_out.last_modified_time); + + len += sizeof("Last-Modified: ") - 1 + sizeof(buf) + 2; + } + } + + if (r->allow_ranges) { + len += sizeof("X-SRCache-Allow-Ranges: 1") - 1 + 2; + } + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + if (ngx_hash_find(&conf->hide_headers_hash, header[i].hash, + header[i].lowcase_key, header[i].key.len)) + { + continue; + } + + len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len + + sizeof(CRLF) - 1; + } + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + /* "HTTP/1.x " */ + b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1); + + /* status line */ + if (status_line) { + b->last = ngx_copy(b->last, status_line->data, status_line->len); + + } else { + b->last = ngx_sprintf(b->last, "%ui", status); + } + *b->last++ = CR; *b->last++ = LF; + + if (!conf->hide_content_type && r->headers_out.content_type.len) { + b->last = ngx_cpymem(b->last, "Content-Type: ", + sizeof("Content-Type: ") - 1); + b->last = ngx_copy(b->last, r->headers_out.content_type.data, + r->headers_out.content_type.len); + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + b->last = ngx_cpymem(b->last, "; charset=", + sizeof("; charset=") - 1); + b->last = ngx_copy(b->last, r->headers_out.charset.data, + r->headers_out.charset.len); + } + + *b->last++ = CR; *b->last++ = LF; + } + + if (!conf->hide_last_modified + && r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + b->last = ngx_cpymem(b->last, "Last-Modified: ", + sizeof("Last-Modified: ") - 1); + + b->last = ngx_cpymem(b->last, buf, sizeof(buf)); + + *b->last++ = CR; *b->last++ = LF; + } + + if (r->allow_ranges) { + b->last = ngx_cpymem(b->last, "X-SRCache-Allow-Ranges: 1\r\n", + sizeof("X-SRCache-Allow-Ranges: 1\r\n") - 1); + } + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + dd("header hash: %lu, hash lc: %lu", (unsigned long) header[i].hash, + (unsigned long) ngx_hash_key_lc(header[i].key.data, + header[i].key.len)); + + if (ngx_hash_find(&conf->hide_headers_hash, header[i].hash, + header[i].lowcase_key, header[i].key.len)) + { + dd("skipped header key: %.*s", (int) header[i].key.len, + header[i].key.data); + continue; + } + + dd("header not skipped"); + + b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); + *b->last++ = ':'; *b->last++ = ' '; + + b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); + *b->last++ = CR; *b->last++ = LF; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "srcache store header %*s", (size_t) (b->last - b->pos), + b->pos); + + /* the end of HTTP header */ + *b->last++ = CR; *b->last++ = LF; + + if (b->last != b->end) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "srcache_fetch: buffer error when serializing the " + "response header: %O left", (off_t) (b->last - b->end)); + + return NGX_ERROR; + } + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + ctx->body_to_cache = cl; + + ctx->response_length += len; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_srcache_hide_headers_hash(ngx_conf_t *cf, + ngx_http_srcache_loc_conf_t *conf, ngx_http_srcache_loc_conf_t *prev, + ngx_str_t *default_hide_headers, ngx_hash_init_t *hash) +{ + ngx_str_t *h; + ngx_uint_t i, j; + ngx_array_t hide_headers; + ngx_hash_key_t *hk; + + if (conf->hide_headers == NGX_CONF_UNSET_PTR + && conf->pass_headers == NGX_CONF_UNSET_PTR) + { + conf->hide_headers_hash = prev->hide_headers_hash; + + if (conf->hide_headers_hash.buckets) { + return NGX_OK; + } + + conf->hide_headers = prev->hide_headers; + conf->pass_headers = prev->pass_headers; + conf->hide_content_type = prev->hide_content_type; + conf->hide_last_modified = prev->hide_last_modified; + + } else { + if (conf->hide_headers == NGX_CONF_UNSET_PTR) { + conf->hide_headers = prev->hide_headers; + } + + if (conf->pass_headers == NGX_CONF_UNSET_PTR) { + conf->pass_headers = prev->pass_headers; + } + } + + dd("init hide headers"); + + if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + for (h = default_hide_headers; h->len; h++) { + hk = ngx_array_push(&hide_headers); + if (hk == NULL) { + return NGX_ERROR; + } + + hk->key = *h; + hk->key_hash = ngx_hash_key_lc(h->data, h->len); + hk->value = (void *) 1; + } + + if (conf->hide_headers != NGX_CONF_UNSET_PTR) { + dd("hide headers not empty"); + + h = conf->hide_headers->elts; + + for (i = 0; i < conf->hide_headers->nelts; i++) { + + hk = hide_headers.elts; + + for (j = 0; j < hide_headers.nelts; j++) { + if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) { + goto exist; + } + } + + hk = ngx_array_push(&hide_headers); + if (hk == NULL) { + return NGX_ERROR; + } + + hk->key = h[i]; + hk->key_hash = ngx_hash_key_lc(h[i].data, h[i].len); + hk->value = (void *) 1; + + if (h[i].len == sizeof("Last-Modified") - 1 + && ngx_strncasecmp(h[i].data, (u_char *) "Last-Modified", + sizeof("Last-Modified") - 1) + == 0) + { + conf->hide_last_modified = 1; + } + + if (h[i].len == sizeof("Content-Type") - 1 + && ngx_strncasecmp(h[i].data, (u_char *) "Content-Type", + sizeof("Content-Type") - 1) == 0) + { + conf->hide_content_type = 1; + } + + dd("adding header to hide headers: %.*s", (int) h[i].len, + h[i].data); + + exist: + + continue; + } + } + + if (conf->pass_headers != NGX_CONF_UNSET_PTR) { + + h = conf->pass_headers->elts; + hk = hide_headers.elts; + + for (i = 0; i < conf->pass_headers->nelts; i++) { + for (j = 0; j < hide_headers.nelts; j++) { + + if (hk[j].key.data == NULL) { + continue; + } + + if (h[i].len == sizeof("Content-Type") - 1 + && ngx_strncasecmp(h[i].data, (u_char *) "Content-Type", + sizeof("Content-Type") - 1) == 0) + { + conf->hide_content_type = 0; + } + + if (h[i].len == sizeof("Last-Modified") - 1 + && ngx_strncasecmp(h[i].data, (u_char *) "Last-Modified", + sizeof("Last-Modified") - 1) == 0) + { + conf->hide_last_modified = 0; + } + + if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) { + hk[j].key.data = NULL; + break; + } + } + } + } + + hash->hash = &conf->hide_headers_hash; + hash->key = ngx_hash_key_lc; + hash->pool = cf->pool; + hash->temp_pool = NULL; + + return ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts); +} + + +ngx_int_t +ngx_http_srcache_cmp_int(const void *one, const void *two) +{ + const ngx_int_t *a = one; + const ngx_int_t *b = two; + + return (*a < *b); +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ |