MEDIUM: cache: store sha1 for hashing the cache key

The cache was relying on the txn->uri for creating its key, which was a
big problem when there was no log activated.

This patch does a sha1 of the host + uri, and stores it in the txn.
When a object is stored, the eb32node uses the first 32 bits of the hash
as a key, and the whole hash is stored in the cache entry.

During a lookup, the truncated hash is used, and when it matches an
entry we check the real sha1.
This commit is contained in:
William Lallemand 2017-11-23 19:43:17 +01:00 committed by Willy Tarreau
parent 7fc96d5a01
commit f528fff46b
2 changed files with 52 additions and 9 deletions

View File

@ -319,6 +319,7 @@ struct http_txn {
/* 1 unused byte here */ /* 1 unused byte here */
short status; /* HTTP status from the server, negative if from proxy */ short status; /* HTTP status from the server, negative if from proxy */
char cache_hash[20]; /* Store the cache hash */
char *uri; /* first line if log needed, NULL otherwise */ char *uri; /* first line if log needed, NULL otherwise */
char *cli_cookie; /* cookie presented by the client, in capture mode */ char *cli_cookie; /* cookie presented by the client, in capture mode */
char *srv_cookie; /* cookie presented by the server, in capture mode */ char *srv_cookie; /* cookie presented by the server, in capture mode */

View File

@ -11,6 +11,7 @@
*/ */
#include <eb32tree.h> #include <eb32tree.h>
#include <import/sha1.h>
#include <types/action.h> #include <types/action.h>
#include <types/cli.h> #include <types/cli.h>
@ -63,6 +64,7 @@ struct cache_entry {
unsigned int latest_validation; /* latest validation date */ unsigned int latest_validation; /* latest validation date */
unsigned int expire; /* expiration date */ unsigned int expire; /* expiration date */
struct eb32_node eb; /* ebtree node used to hold the cache object */ struct eb32_node eb; /* ebtree node used to hold the cache object */
char hash[20];
unsigned char data[0]; unsigned char data[0];
}; };
@ -71,16 +73,21 @@ struct cache_entry {
static struct list caches = LIST_HEAD_INIT(caches); static struct list caches = LIST_HEAD_INIT(caches);
static struct cache *tmp_cache_config = NULL; static struct cache *tmp_cache_config = NULL;
struct cache_entry *entry_exist(struct cache *cache, struct cache_entry *new_entry) struct cache_entry *entry_exist(struct cache *cache, char *hash)
{ {
struct eb32_node *node; struct eb32_node *node;
struct cache_entry *entry; struct cache_entry *entry;
node = eb32_lookup(&cache->entries, new_entry->eb.key); node = eb32_lookup(&cache->entries, (*(unsigned int *)hash));
if (!node) if (!node)
return NULL; return NULL;
entry = eb32_entry(node, struct cache_entry, eb); entry = eb32_entry(node, struct cache_entry, eb);
/* if that's not the right node */
if (memcmp(entry->hash, hash, sizeof(entry->hash)))
return NULL;
if (entry->expire > now.tv_sec) { if (entry->expire > now.tv_sec) {
return entry; return entry;
} else { } else {
@ -443,11 +450,12 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
cache_ctx->first_block = first; cache_ctx->first_block = first;
object = (struct cache_entry *)first->data; object = (struct cache_entry *)first->data;
object->eb.key = hash_djb2(txn->uri, strlen(txn->uri)); object->eb.key = (*(unsigned int *)&txn->cache_hash);
memcpy(object->hash, txn->cache_hash, sizeof(object->hash));
/* Insert the node later on caching success */ /* Insert the node later on caching success */
shctx_lock(shctx); shctx_lock(shctx);
if (entry_exist((struct cache *)rule->arg.act.p[0], object)) { if (entry_exist((struct cache *)rule->arg.act.p[0], txn->cache_hash)) {
shctx_unlock(shctx); shctx_unlock(shctx);
if (filter->ctx) { if (filter->ctx) {
object->eb.key = 0; object->eb.key = 0;
@ -592,19 +600,53 @@ enum act_parse_ret parse_cache_store(const char **args, int *orig_arg, struct pr
return ACT_RET_PRS_ERR; return ACT_RET_PRS_ERR;
} }
/* This produces a sha1 hash of the concatenation of the first
* occurrence of the Host header followed by the path component if it
* begins with a slash ('/'). */
int sha1_hosturi(struct http_txn *txn)
{
struct hdr_ctx ctx;
blk_SHA_CTX sha1_ctx;
struct chunk *trash;
char *path;
char *end;
trash = get_trash_chunk();
/* retrive the host */
ctx.idx = 0;
if (!http_find_header2("Host", 4, txn->req.chn->buf->p, &txn->hdr_idx, &ctx))
return 0;
chunk_strncat(trash, ctx.line + ctx.val, ctx.vlen);
/* now retrieve the path */
end = txn->req.chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
path = http_get_path(txn);
if (!path)
return 0;
chunk_strncat(trash, path, end - path);
/* hash everything */
blk_SHA1_Init(&sha1_ctx);
blk_SHA1_Update(&sha1_ctx, trash->str, trash->len);
blk_SHA1_Final((unsigned char *)txn->cache_hash, &sha1_ctx);
return 1;
}
enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *px, enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *px,
struct session *sess, struct stream *s, int flags) struct session *sess, struct stream *s, int flags)
{ {
struct cache_entry search_entry;
struct cache_entry *res; struct cache_entry *res;
struct cache *cache = (struct cache *)rule->arg.act.p[0]; struct cache *cache = (struct cache *)rule->arg.act.p[0];
search_entry.eb.key = hash_djb2(s->txn->uri, strlen(s->txn->uri)); sha1_hosturi(s->txn);
shctx_lock(shctx_ptr(cache)); shctx_lock(shctx_ptr(cache));
res = entry_exist(cache, &search_entry); res = entry_exist(cache, s->txn->cache_hash);
if (res) { if (res) {
struct appctx *appctx; struct appctx *appctx;
shctx_row_inc_hot(shctx_ptr(cache), block_ptr(res)); shctx_row_inc_hot(shctx_ptr(cache), block_ptr(res));
@ -900,7 +942,7 @@ static int cli_io_handler_show_cache(struct appctx *appctx)
} }
entry = container_of(node, struct cache_entry, eb); entry = container_of(node, struct cache_entry, eb);
chunk_appendf(&trash, "%p (size: %u (%u blocks), refcount:%u, expire: %d)\n", entry, block_ptr(entry)->len, block_ptr(entry)->block_count, block_ptr(entry)->refcount, entry->expire - (int)now.tv_sec); chunk_appendf(&trash, "%p hash:%u size:%u (%u blocks), refcount:%u, expire:%d\n", entry, (*(unsigned int *)entry->hash), block_ptr(entry)->len, block_ptr(entry)->block_count, block_ptr(entry)->refcount, entry->expire - (int)now.tv_sec);
next_key = node->key + 1; next_key = node->key + 1;
appctx->ctx.cli.i0 = next_key; appctx->ctx.cli.i0 = next_key;