summaryrefslogtreecommitdiffstats
path: root/src/cache.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/cache.c251
1 files changed, 203 insertions, 48 deletions
diff --git a/src/cache.c b/src/cache.c
index 9f12f10..32f2e47 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -77,6 +77,7 @@ struct cache_appctx {
unsigned int rem_data; /* Remaining bytes for the last data block (HTX only, 0 means process next block) */
unsigned int send_notmodified:1; /* In case of conditional request, we might want to send a "304 Not Modified" response instead of the stored data. */
unsigned int unused:31;
+ /* 4 bytes hole here */
struct shared_block *next; /* The next block of data to be sent for this cache entry. */
};
@@ -193,7 +194,7 @@ struct cache_entry {
unsigned int latest_validation; /* latest validation date */
unsigned int expire; /* expiration date (wall clock time) */
unsigned int age; /* Origin server "Age" header value */
-
+ unsigned int body_size; /* Size of the body */
int refcount;
struct eb32_node eb; /* ebtree node used to hold the cache object */
@@ -231,8 +232,8 @@ DECLARE_STATIC_POOL(pool_head_cache_st, "cache_st", sizeof(struct cache_st));
static struct eb32_node *insert_entry(struct cache *cache, struct cache_tree *tree, struct cache_entry *new_entry);
static void delete_entry(struct cache_entry *del_entry);
-static void release_entry_locked(struct cache_tree *cache, struct cache_entry *entry);
-static void release_entry_unlocked(struct cache_tree *cache, struct cache_entry *entry);
+static inline void release_entry_locked(struct cache_tree *cache, struct cache_entry *entry);
+static inline void release_entry_unlocked(struct cache_tree *cache, struct cache_entry *entry);
/*
* Find a cache_entry in the <cache>'s tree that has the hash <hash>.
@@ -753,6 +754,7 @@ cache_store_http_payload(struct stream *s, struct filter *filter, struct http_ms
struct htx_blk *blk;
struct shared_block *fb;
struct htx_ret htxret;
+ size_t data_len = 0;
unsigned int orig_len, to_forward;
int ret;
@@ -789,6 +791,7 @@ cache_store_http_payload(struct stream *s, struct filter *filter, struct http_ms
chunk_memcat(&trash, (char *)&info, sizeof(info));
chunk_istcat(&trash, v);
to_forward += v.len;
+ data_len += v.len;
len -= v.len;
break;
@@ -817,6 +820,8 @@ cache_store_http_payload(struct stream *s, struct filter *filter, struct http_ms
goto no_cache;
}
+ /* disguise below to shut a warning on */
+ DISGUISE((struct cache_entry *)st->first_block->data)->body_size += data_len;
ret = shctx_row_data_append(shctx, st->first_block,
(unsigned char *)b_head(&trash), b_data(&trash));
if (ret < 0)
@@ -1133,7 +1138,7 @@ static int http_check_vary_header(struct htx *htx, unsigned int *vary_signature)
* "vary" on the accept-encoding value.
* Returns 0 if we found a known encoding in the response, -1 otherwise.
*/
-static int set_secondary_key_encoding(struct htx *htx, char *secondary_key)
+static int set_secondary_key_encoding(struct htx *htx, unsigned int vary_signature, char *secondary_key)
{
unsigned int resp_encoding_bitmap = 0;
const struct vary_hashing_information *info = vary_information;
@@ -1143,6 +1148,11 @@ static int set_secondary_key_encoding(struct htx *htx, char *secondary_key)
unsigned int encoding_value;
struct http_hdr_ctx ctx = { .blk = NULL };
+ /* We must not set the accept encoding part of the secondary signature
+ * if the response does not vary on 'Accept Encoding'. */
+ if (!(vary_signature & VARY_ACCEPT_ENCODING))
+ return 0;
+
/* Look for the accept-encoding part of the secondary_key. */
while (count < hash_info_count && info->value != VARY_ACCEPT_ENCODING) {
offset += info->hash_length;
@@ -1404,7 +1414,7 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
* We will not cache a response that has an unknown encoding (not
* explicitly supported in parse_encoding_value function). */
if (cache->vary_processing_enabled && vary_signature)
- if (set_secondary_key_encoding(htx, object->secondary_key))
+ if (set_secondary_key_encoding(htx, vary_signature, object->secondary_key))
goto out;
if (!shctx_row_reserve_hot(shctx, first, trash.data)) {
@@ -1480,8 +1490,7 @@ static unsigned int htx_cache_dump_blk(struct appctx *appctx, struct htx *htx, e
unsigned int max, total;
uint32_t blksz;
- max = htx_get_max_blksz(htx,
- channel_htx_recv_max(sc_ic(appctx_sc(appctx)), htx));
+ max = htx_free_data_space(htx);
if (!max)
return 0;
blksz = ((type == HTX_BLK_HDR || type == HTX_BLK_TLR)
@@ -1521,14 +1530,14 @@ static unsigned int htx_cache_dump_data_blk(struct appctx *appctx, struct htx *h
struct cache_appctx *ctx = appctx->svcctx;
struct cache_flt_conf *cconf = appctx->rule->arg.act.p[0];
struct shared_context *shctx = shctx_ptr(cconf->c.cache);
- unsigned int max, total, rem_data;
+ unsigned int max, total, rem_data, data_len;
uint32_t blksz;
- max = htx_get_max_blksz(htx,
- channel_htx_recv_max(sc_ic(appctx_sc(appctx)), htx));
+ max = htx_free_data_space(htx);
if (!max)
return 0;
+ data_len = 0;
rem_data = 0;
if (ctx->rem_data) {
blksz = ctx->rem_data;
@@ -1551,6 +1560,7 @@ static unsigned int htx_cache_dump_data_blk(struct appctx *appctx, struct htx *h
offset += sz;
blksz -= sz;
total += sz;
+ data_len += sz;
if (sz < max)
break;
if (blksz || offset == shctx->block_size) {
@@ -1563,6 +1573,7 @@ static unsigned int htx_cache_dump_data_blk(struct appctx *appctx, struct htx *h
ctx->next = shblk;
ctx->sent += total;
ctx->rem_data = rem_data + blksz;
+ appctx->to_forward -= data_len;
return total;
}
@@ -1619,6 +1630,108 @@ static size_t htx_cache_dump_msg(struct appctx *appctx, struct htx *htx, unsigne
return total;
}
+static unsigned int ff_cache_dump_data_blk(struct appctx *appctx, struct buffer *buf, unsigned int len,
+ uint32_t info, struct shared_block *shblk, unsigned int offset)
+{
+ struct cache_appctx *ctx = appctx->svcctx;
+ struct cache_flt_conf *cconf = appctx->rule->arg.act.p[0];
+ struct shared_context *shctx = shctx_ptr(cconf->c.cache);
+ unsigned int total, rem_data, data_len;
+ uint32_t blksz;
+
+ total = 0;
+ data_len = 0;
+ rem_data = 0;
+ if (ctx->rem_data)
+ blksz = ctx->rem_data;
+ else {
+ blksz = (info & 0xfffffff);
+ ctx->sent += 4;
+ }
+ if (blksz > len) {
+ rem_data = blksz - len;
+ blksz = len;
+ }
+
+ while (blksz) {
+ size_t sz;
+
+ len = MIN(blksz, shctx->block_size - offset);
+ sz = b_putblk(buf, (char *)(shblk->data + offset), len);
+ offset += sz;
+ blksz -= sz;
+ total += sz;
+ data_len += sz;
+ if (sz < len)
+ break;
+ if (blksz || offset == shctx->block_size) {
+ shblk = LIST_NEXT(&shblk->list, typeof(shblk), list);
+ offset = 0;
+ }
+ }
+
+ ctx->offset = offset;
+ ctx->next = shblk;
+ ctx->sent += total;
+ ctx->rem_data = rem_data + blksz;
+ appctx->to_forward -= data_len;
+ return total;
+}
+
+static size_t ff_cache_dump_msg(struct appctx *appctx, struct buffer *buf, unsigned int len)
+{
+ struct cache_appctx *ctx = appctx->svcctx;
+ struct cache_entry *cache_ptr = ctx->entry;
+ struct shared_block *first = block_ptr(cache_ptr);
+ struct cache_flt_conf *cconf = appctx->rule->arg.act.p[0];
+ struct shared_context *shctx = shctx_ptr(cconf->c.cache);
+ struct shared_block *shblk;
+ unsigned int offset, sz;
+ unsigned int ret, total = 0;
+
+ while (len && (ctx->sent != first->len - sizeof(*cache_ptr))) {
+ enum htx_blk_type type;
+ uint32_t info;
+
+ shblk = ctx->next;
+ offset = ctx->offset;
+ if (ctx->rem_data) {
+ type = HTX_BLK_DATA;
+ info = 0;
+ goto add_data_blk;
+ }
+
+ /* Get info of the next HTX block. May be split on 2 shblk */
+ sz = MIN(4, shctx->block_size - offset);
+ memcpy((char *)&info, (const char *)shblk->data + offset, sz);
+ offset += sz;
+ if (sz < 4) {
+ shblk = LIST_NEXT(&shblk->list, typeof(shblk), list);
+ memcpy(((char *)&info)+sz, (const char *)shblk->data, 4 - sz);
+ offset = (4 - sz);
+ }
+
+ /* Get payload of the next HTX block and insert it. */
+ type = (info >> 28);
+ if (type == HTX_BLK_DATA) {
+ add_data_blk:
+ ret = ff_cache_dump_data_blk(appctx, buf, len, info, shblk, offset);
+ }
+ else
+ ret = 0;
+
+ if (!ret)
+ break;
+ total += ret;
+ len -= ret;
+
+ if (ctx->rem_data)
+ break;
+ }
+
+ return total;
+}
+
static int htx_cache_add_age_hdr(struct appctx *appctx, struct htx *htx)
{
struct cache_appctx *ctx = appctx->svcctx;
@@ -1637,31 +1750,58 @@ static int htx_cache_add_age_hdr(struct appctx *appctx, struct htx *htx)
return 1;
}
+static size_t http_cache_fastfwd(struct appctx *appctx, struct buffer *buf, size_t count, unsigned int flags)
+{
+ struct cache_appctx *ctx = appctx->svcctx;
+ struct cache_entry *cache_ptr = ctx->entry;
+ struct shared_block *first = block_ptr(cache_ptr);
+ size_t ret;
+
+ BUG_ON(!appctx->to_forward || count > appctx->to_forward);
+
+ ret = ff_cache_dump_msg(appctx, buf, count);
+
+ if (!appctx->to_forward) {
+ se_fl_clr(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD);
+ applet_fl_clr(appctx, APPCTX_FL_FASTFWD);
+ if (ctx->sent == first->len - sizeof(*cache_ptr)) {
+ applet_set_eoi(appctx);
+ applet_set_eos(appctx);
+ appctx->st0 = HTX_CACHE_END;
+ }
+ }
+ return ret;
+}
+
static void http_cache_io_handler(struct appctx *appctx)
{
struct cache_appctx *ctx = appctx->svcctx;
struct cache_entry *cache_ptr = ctx->entry;
struct shared_block *first = block_ptr(cache_ptr);
- struct stconn *sc = appctx_sc(appctx);
- struct channel *req = sc_oc(sc);
- struct channel *res = sc_ic(sc);
- struct htx *req_htx, *res_htx;
+ struct htx *res_htx = NULL;
struct buffer *errmsg;
unsigned int len;
- size_t ret, total = 0;
+ size_t ret;
- res_htx = htx_from_buf(&res->buf);
- total = res_htx->data;
+ if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_ALLOC|APPCTX_FL_OUTBLK_FULL))
+ goto exit;
- if (unlikely(se_fl_test(appctx->sedesc, (SE_FL_EOS|SE_FL_ERROR|SE_FL_SHR|SE_FL_SHW))))
- goto out;
+ if (applet_fl_test(appctx, APPCTX_FL_FASTFWD) && se_fl_test(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD))
+ goto exit;
- /* Check if the input buffer is available. */
- if (!b_size(&res->buf)) {
- sc_need_room(sc, 0);
- goto out;
+ if (!appctx_get_buf(appctx, &appctx->outbuf)) {
+ goto exit;
+ }
+
+ if (unlikely(applet_fl_test(appctx, APPCTX_FL_EOS|APPCTX_FL_ERROR))) {
+ goto exit;
}
+ res_htx = htx_from_buf(&appctx->outbuf);
+
+ len = first->len - sizeof(*cache_ptr) - ctx->sent;
+ res_htx = htx_from_buf(&appctx->outbuf);
+
if (appctx->st0 == HTX_CACHE_INIT) {
ctx->next = block_ptr(cache_ptr);
ctx->offset = sizeof(*cache_ptr);
@@ -1671,8 +1811,13 @@ static void http_cache_io_handler(struct appctx *appctx)
}
if (appctx->st0 == HTX_CACHE_HEADER) {
+ struct ist meth;
+
+ if (unlikely(applet_fl_test(appctx, APPCTX_FL_INBLK_ALLOC))) {
+ goto exit;
+ }
+
/* Headers must be dump at once. Otherwise it is an error */
- len = first->len - sizeof(*cache_ptr) - ctx->sent;
ret = htx_cache_dump_msg(appctx, res_htx, len, HTX_BLK_EOH);
if (!ret || (htx_get_tail_type(res_htx) != HTX_BLK_EOH) ||
!htx_cache_add_age_hdr(appctx, res_htx))
@@ -1689,60 +1834,66 @@ static void http_cache_io_handler(struct appctx *appctx)
/* Skip response body for HEAD requests or in case of "304 Not
* Modified" response. */
- if (__sc_strm(sc)->txn->meth == HTTP_METH_HEAD || ctx->send_notmodified)
+ meth = htx_sl_req_meth(http_get_stline(htxbuf(&appctx->inbuf)));
+ if (find_http_meth(istptr(meth), istlen(meth)) == HTTP_METH_HEAD || ctx->send_notmodified)
appctx->st0 = HTX_CACHE_EOM;
- else
+ else {
+ if (!(global.tune.no_zero_copy_fwd & NO_ZERO_COPY_FWD_APPLET))
+ se_fl_set(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD);
+
+ appctx->to_forward = cache_ptr->body_size;
+ len = first->len - sizeof(*cache_ptr) - ctx->sent;
appctx->st0 = HTX_CACHE_DATA;
+ }
}
if (appctx->st0 == HTX_CACHE_DATA) {
- len = first->len - sizeof(*cache_ptr) - ctx->sent;
if (len) {
ret = htx_cache_dump_msg(appctx, res_htx, len, HTX_BLK_UNUSED);
if (ret < len) {
- sc_need_room(sc, channel_htx_recv_max(res, res_htx) + 1);
+ applet_fl_set(appctx, APPCTX_FL_OUTBLK_FULL);
goto out;
}
}
+ BUG_ON(appctx->to_forward);
appctx->st0 = HTX_CACHE_EOM;
}
if (appctx->st0 == HTX_CACHE_EOM) {
/* no more data are expected. */
res_htx->flags |= HTX_FL_EOM;
- se_fl_set(appctx->sedesc, SE_FL_EOI);
-
+ applet_set_eoi(appctx);
+ se_fl_clr(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD);
+ applet_fl_clr(appctx, APPCTX_FL_FASTFWD);
appctx->st0 = HTX_CACHE_END;
}
end:
- if (appctx->st0 == HTX_CACHE_END)
- se_fl_set(appctx->sedesc, SE_FL_EOS);
+ if (appctx->st0 == HTX_CACHE_END) {
+ applet_set_eos(appctx);
+ }
out:
- total = res_htx->data - total;
- if (total)
- channel_add_input(res, total);
- htx_to_buf(res_htx, &res->buf);
+ if (res_htx)
+ htx_to_buf(res_htx, &appctx->outbuf);
+ exit:
/* eat the whole request */
- if (co_data(req)) {
- req_htx = htx_from_buf(&req->buf);
- co_htx_skip(req, req_htx, co_data(req));
- htx_to_buf(req_htx, &req->buf);
- }
+ b_reset(&appctx->inbuf);
+ applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
+ appctx->sedesc->iobuf.flags &= ~IOBUF_FL_FF_BLOCKED;
return;
error:
/* Sent and HTTP error 500 */
- b_reset(&res->buf);
+ b_reset(&appctx->outbuf);
errmsg = &http_err_chunks[HTTP_ERR_500];
- res->buf.data = b_data(errmsg);
- memcpy(res->buf.area, b_head(errmsg), b_data(errmsg));
- res_htx = htx_from_buf(&res->buf);
+ appctx->outbuf.data = b_data(errmsg);
+ memcpy(appctx->outbuf.area, b_head(errmsg), b_data(errmsg));
+ res_htx = htx_from_buf(&appctx->outbuf);
- total = 0;
- se_fl_set(appctx->sedesc, SE_FL_ERROR);
+ applet_set_eos(appctx);
+ applet_set_error(appctx);
appctx->st0 = HTX_CACHE_END;
goto end;
}
@@ -2324,7 +2475,7 @@ int post_check_cache()
list_for_each_entry_safe(cache_config, back, &caches_config, list) {
ret_shctx = shctx_init(&shctx, cache_config->maxblocks, CACHE_BLOCKSIZE,
- cache_config->maxobjsz, sizeof(struct cache));
+ cache_config->maxobjsz, sizeof(struct cache), cache_config->id);
if (ret_shctx <= 0) {
if (ret_shctx == SHCTX_E_INIT_LOCK)
@@ -2995,9 +3146,13 @@ struct applet http_cache_applet = {
.obj_type = OBJ_TYPE_APPLET,
.name = "<CACHE>", /* used for logging */
.fct = http_cache_io_handler,
+ .rcv_buf = appctx_htx_rcv_buf,
+ .snd_buf = appctx_htx_snd_buf,
+ .fastfwd = http_cache_fastfwd,
.release = http_cache_applet_release,
};
+
/* config parsers for this section */
REGISTER_CONFIG_SECTION("cache", cfg_parse_cache, cfg_post_parse_section_cache);
REGISTER_POST_CHECK(post_check_cache);