static ngx_int_t ngx_http_reqstat_input_body_filter(ngx_http_request_t *r, ngx_buf_t *buf) { ngx_uint_t diff; ngx_int_t rc; ngx_http_reqstat_conf_t *slcf; ngx_http_reqstat_store_t *store; slcf = ngx_http_get_module_loc_conf(r, ngx_http_reqstat_module); if (slcf->monitor == NULL) { return ngx_http_next_input_body_filter(r, buf); } store = ngx_pcalloc(r->pool, sizeof(ngx_http_reqstat_store_t)); if (store == NULL) { return NGX_ERROR; } rc = ngx_http_test_predicates(r, slcf->bypass); if(rc == NGX_ERROR) { return NGX_ERROR; } else if(rc == NGX_DECLINED) { store->bypass = 1; } ngx_http_set_ctx(r, store, ngx_http_reqstat_module); if (store->bypass) { return ngx_http_next_input_body_filter(r, buf); } diff = r->connection->received - store->recv; store->recv = r->connection->received; store->bytes_in = diff; return ngx_http_next_input_body_filter(r, buf); }
static ngx_http_reqstat_store_t * ngx_http_reqstat_create_store(ngx_http_request_t *r, ngx_http_reqstat_conf_t *rlcf) { ngx_str_t val; ngx_uint_t i; ngx_shm_zone_t **shm_zone, *z; ngx_http_reqstat_ctx_t *ctx; ngx_http_reqstat_store_t *store; ngx_http_reqstat_rbnode_t *fnode, **fnode_store; store = ngx_pcalloc(r->pool, sizeof(ngx_http_reqstat_store_t)); if (store == NULL) { return NULL; } if (rlcf->monitor == NULL) { store->bypass = 1; return store; } store->conf = rlcf; switch (ngx_http_test_predicates(r, rlcf->bypass)) { case NGX_ERROR: return NULL; case NGX_DECLINED: store->bypass = 1; return store; default: /* NGX_OK */ break; } if (ngx_array_init(&store->monitor_index, r->pool, rlcf->monitor->nelts, sizeof(ngx_http_reqstat_rbnode_t *)) == NGX_ERROR) { return NULL; } shm_zone = rlcf->monitor->elts; for (i = 0; i < rlcf->monitor->nelts; i++) { z = shm_zone[i]; ctx = z->data; if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "failed to reap the key \"%V\"", ctx->val); continue; } fnode = ngx_http_reqstat_rbtree_lookup(shm_zone[i], &val); if (fnode == NULL) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "failed to alloc node in zone \"%V\", " "enlarge it please", &z->shm.name); } else { fnode_store = ngx_array_push(&store->monitor_index); *fnode_store = fnode; } } return store; }
static ngx_int_t ngx_http_filter_cache_header_filter(ngx_http_request_t *r) { ngx_http_filter_cache_ctx_t *ctx = NULL; ngx_http_filter_cache_conf_t *conf = NULL; time_t now, valid; ngx_temp_file_t *tf; ngx_chain_t out; ssize_t offset; ngx_list_part_t *part; ngx_table_elt_t *h; ngx_uint_t i; u_char *p; size_t len; ngx_pool_cleanup_t *cln = NULL; ngx_http_filter_cache_meta_t meta; if(r != r->main) { /*just skip as we got headers in main*/ return ngx_http_next_header_filter(r); } conf = ngx_http_get_module_loc_conf(r, ngx_http_filter_cache_module); switch (ngx_http_test_predicates(r, conf->upstream.no_cache)) { case NGX_ERROR: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, __FILE__" ngx_http_test_predicates returned an error for no_cache"); return NGX_ERROR; case NGX_DECLINED: goto nocache; default: /* NGX_OK */ break; } ctx = r->filter_cache; if(!ctx || (FILTER_DONOTCACHE == ctx->cacheable)) { goto nocache; } /* ngx_http_filter_cache_create(r); */ if (ctx->cache && ctx->cache->file.fd != NGX_INVALID_FILE) { ngx_pool_run_cleanup_file(r->pool, ctx->cache->file.fd); ctx->cache->file.fd = NGX_INVALID_FILE; } ctx->cache->valid_sec = 0; now = ngx_time(); valid = 0; valid = ngx_http_filter_cache_valid(conf->upstream.cache_valid, r->headers_out.status); if (valid) { ctx->cache->valid_sec = now + valid; } else { goto nocache; } tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); if (tf == NULL) { return NGX_ERROR; } tf->file.fd = NGX_INVALID_FILE; tf->file.log = r->connection->log; tf->path = conf->upstream.temp_path; tf->pool = r->pool; tf->persistent = 1; if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, tf->persistent, tf->clean, tf->access) != NGX_OK) { return NGX_ERROR; } ctx->tf = tf; cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { return NGX_ERROR; } cln->handler = filter_cache_cleanup; cln->data = ctx; ctx->buffer.pos = ctx->buffer.start = ngx_palloc(r->pool, conf->upstream.buffer_size); ctx->buffer.end = ctx->buffer.start + conf->upstream.buffer_size; ctx->buffer.temporary = 1; ctx->buffer.memory = 1; ctx->buffer.last_buf = 1; ctx->buffer.pos += ctx->cache->header_start; ctx->cache->last_modified = r->headers_out.last_modified_time; ctx->cache->date = now; /* Headers */ /* fill in the metadata*/ meta.status = r->headers_out.status; #if (NGX_HTTP_GZIP) meta.gzip_vary = r->gzip_vary; /* Note: there is still some wierdness to how gzip_vary works...*/ #endif meta.last_modified_time = r->headers_out.last_modified_time; ngx_memcpy((void *)(ctx->buffer.pos), (void *)(&meta), sizeof(ngx_http_filter_cache_meta_t) ); ctx->buffer.pos += sizeof(ngx_http_filter_cache_meta_t); /* Headers taht aren't in teh table for some reason */ /*Do we need to try to set it if it's not set???*/ /* Content Type */ if ( r->headers_out.content_type.data ) { p = memchr((void *)r->headers_out.content_type.data, ';', r->headers_out.content_type.len ); if ( p ) { len = p - r->headers_out.content_type.data; ngx_cpystrn( ctx->buffer.pos, r->headers_out.content_type.data, len + 1); ctx->buffer.pos += len + 1; } else { ngx_cpystrn( ctx->buffer.pos, r->headers_out.content_type.data, r->headers_out.content_type.len + 1 ); ctx->buffer.pos += r->headers_out.content_type.len + 1; } } else { *ctx->buffer.pos = (u_char)'\0'; ctx->buffer.pos++; } /* Charset */ if ( r->headers_out.charset.data ) { ngx_cpystrn( ctx->buffer.pos, r->headers_out.charset.data, r->headers_out.charset.len + 1 ); ctx->buffer.pos += r->headers_out.charset.len + 1; } else { *ctx->buffer.pos = (u_char)'\0'; ctx->buffer.pos++; } /* Content Encoding */ if ( r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) { ngx_cpystrn( ctx->buffer.pos, r->headers_out.content_encoding->value.data, r->headers_out.content_encoding->value.len + 1 ); ctx->buffer.pos += r->headers_out.content_encoding->value.len + 1; } else { *ctx->buffer.pos = (u_char)'\0'; ctx->buffer.pos++; } /* Last-Modified */ if(r->headers_out.last_modified_time && r->headers_out.last_modified && r->headers_out.last_modified->value.len) { ngx_cpystrn( ctx->buffer.pos, r->headers_out.last_modified->value.data, r->headers_out.last_modified->value.len + 1 ); ctx->buffer.pos += r->headers_out.last_modified->value.len + 1; } else { *ctx->buffer.pos = (u_char)'\0'; ctx->buffer.pos++; } /* XXX: is last-modified special???*/ /* Everything From the Table */ part = &r->headers_out.headers.part; h = part->elts; for (i=0; /* void */; i++) { if ( i >= part->nelts || !part->nelts ) { if ( part->next == NULL ) { ctx->cacheable = FILTER_CACHEABLE; break; } part = part->next; h = part->elts; i = 0; } /*need to be really sure this header is "valid"*/ /* if(h[i].key.len && h[i].value.len && h[i].hash && h[i].lowcase_key) {*/ /* if(!h[i].lowcase_key) { */ /* if((h[i].lowcase_key = ngx_pnalloc(r->pool, h->key.len +1)) == NULL) { */ /* continue; */ /* } */ /* ngx_strlow(h[i].lowcase_key, h[i].key.data, h[i].key.len); */ /* } */ /* if(!h[i].hash) { */ /* h[i].hash = ngx_hash_key_lc(h[i].key.data, h[i].key.len); */ /* } */ /* if (ngx_hash_find(&conf->upstream.hide_headers_hash, h[i].hash, */ /* h[i].lowcase_key, h[i].key.len)) */ /* { */ /* continue; */ /* } */ if(h[i].key.len && h[i].value.len) { if(find_string_in_array(&(h[i].key), conf->upstream.hide_headers)){ continue; } if ( (ngx_uint_t)(h[i].key.len + h[i].value.len + 4) > (ngx_uint_t)(ctx->buffer.last - ctx->buffer.pos) ) { ctx->cacheable = FILTER_DONOTCACHE; break; } ngx_cpystrn( ctx->buffer.pos, h[i].key.data, h[i].key.len + 1 ); ctx->buffer.pos += h[i].key.len + 1; ngx_cpystrn( ctx->buffer.pos, h[i].value.data, h[i].value.len + 1 ); ctx->buffer.pos += h[i].value.len + 1; } } if(FILTER_CACHEABLE != ctx->cacheable) { goto nocache; } ctx->buffer.last = ctx->buffer.pos; ctx->cache->body_start = (u_short) (ctx->buffer.pos - ctx->buffer.start); ngx_http_filter_cache_set_header(r, ctx->buffer.start); ctx->cache->date = now; /*write to temp file*/ ctx->buffer.pos = ctx->buffer.start; out.buf = &ctx->buffer; out.next = NULL; offset = ngx_write_chain_to_temp_file(tf, &out); tf->offset += offset; r->main_filter_need_in_memory = 1; return ngx_http_next_header_filter(r); nocache: if(ctx) { ctx->cacheable = FILTER_DONOTCACHE; /* if(ctx->cache) { */ /* ngx_http_filter_cache_free(ctx->cache, ctx->tf); */ /* } */ } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_filter_cache_handler(ngx_http_request_t *r) { ngx_http_filter_cache_ctx_t *ctx = NULL; ngx_http_filter_cache_conf_t *conf = NULL; ngx_http_variable_value_t *vv = NULL; ngx_http_cache_t *c = NULL; ngx_str_t *key = NULL; ngx_int_t rc = NGX_ERROR; if(r != r->main) { /* we don't currently serve subrequests * if we ever do subrequests, we will need a way to associate a ctx with this request * maybe keep an r in ctx and compare to r here? */ return cache_miss(r, NULL, 0, 0); } conf = ngx_http_get_module_loc_conf(r, ngx_http_filter_cache_module); /* turned off */ if (NULL == conf->upstream.cache) { return cache_miss(r, NULL, 0, conf->handler); } ctx = r->filter_cache; if(ctx) { /*loop detected??*/ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "cache loop in " __FILE__); /* XXX: this causes a 598 to be returned. Is that what we want??? * should loop return yet another status code?? * be configurable and default to 598?? */ return cache_miss(r, NULL, 0, conf->handler); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_filter_cache_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ctx->cache = NULL; ctx->cacheable = FILTER_DONOTCACHE; ctx->cache_status = NGX_HTTP_CACHE_MISS; /* needed so the ctx works in cache status*/ r->filter_cache = ctx; switch (ngx_http_test_predicates(r, conf->upstream.cache_bypass)) { case NGX_ERROR: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, __FILE__" ngx_http_test_predicates returned an error for bypass"); return NGX_ERROR; case NGX_DECLINED: ctx->cache_status = NGX_HTTP_CACHE_BYPASS; return cache_miss(r, NULL, 0, conf->handler); default: /* NGX_OK */ break; } if (!(r->method & conf->upstream.cache_methods)) { return cache_miss(r, NULL, 0, conf->handler); } rc = ngx_http_discard_request_body(r); if (rc != NGX_OK && rc != NGX_AGAIN) { return rc; } vv = ngx_http_get_indexed_variable(r, conf->index); if (vv == NULL || vv->not_found || vv->len == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the \"filter_cache_key\" variable is not set"); return NGX_ERROR; } ctx->key.data = vv->data; ctx->key.len = vv->len; c = ctx->cache = NULL; if (ngx_http_filter_cache_new(r) != NGX_OK) { return NGX_ERROR; } key = ngx_array_push(&ctx->cache->keys); if (key == NULL) { return NGX_ERROR; } key->data = ctx->key.data; key->len = ctx->key.len; ctx->cache->file_cache = conf->upstream.cache->data; ngx_http_filter_cache_create_key(r); /* ngx_http_filter_cache_create(r); */ c = ctx->cache; c->min_uses = conf->upstream.cache_min_uses; c->body_start = conf->upstream.buffer_size; c->file_cache = conf->upstream.cache->data; rc = ngx_http_filter_cache_open(r); switch(rc) { case NGX_HTTP_CACHE_UPDATING: if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING) { if(ctx->cache && conf->grace && ( (ctx->cache->valid_sec - ngx_time() ) < conf->grace)) { ctx->cache_status = NGX_HTTP_CACHE_UPDATING; rc = NGX_OK; } else { rc = NGX_HTTP_CACHE_STALE; } } else { rc = NGX_HTTP_CACHE_STALE; } break; case NGX_OK: ctx->cache_status = NGX_HTTP_CACHE_HIT; } switch (rc) { case NGX_OK: return filter_cache_send(r); break; case NGX_HTTP_CACHE_STALE: ctx->cache_status = NGX_HTTP_CACHE_EXPIRED; /*return 599;*/ break; case NGX_DECLINED: break; case NGX_HTTP_CACHE_SCARCE: return cache_miss(r, ctx, 0, conf->handler); break; case NGX_AGAIN: return NGX_BUSY; case NGX_ERROR: return NGX_ERROR; default: /*????*/ break; } return cache_miss(r, ctx, 1, conf->handler); }