2017-10-30 06:15:51 -04:00
|
|
|
/*
|
|
|
|
|
* Cache management
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2017 HAProxy Technologies
|
|
|
|
|
* William Lallemand <wlallemand@haproxy.com>
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
|
*/
|
|
|
|
|
|
2020-05-27 06:58:42 -04:00
|
|
|
#include <haproxy/api.h>
|
2020-06-03 12:38:48 -04:00
|
|
|
#include <haproxy/shctx.h>
|
2020-05-27 04:58:19 -04:00
|
|
|
#include <import/eb32tree.h>
|
2017-11-23 13:43:17 -05:00
|
|
|
#include <import/sha1.h>
|
2017-10-30 06:15:51 -04:00
|
|
|
|
2017-11-21 14:01:24 -05:00
|
|
|
#include <types/action.h>
|
MEDIUM: cache: "show cache" on the cli
The cli command "show cache" displays the status of the cache, the first
displayed line is the shctx informations with how much blocks available
blocks it contains (blocks are 1k by default).
The next lines are the objects stored in the cache tree, the pointer,
the size of the object and how much blocks it uses, a refcount for the
number of users of the object, and the remaining expiration time (which
can be negative if expired)
Example:
$ echo "show cache" | socat - /run/haproxy.sock
0x7fa54e9ab03a: foobar (shctx:0x7fa54e9ab000, available blocks:3921)
0x7fa54ed65b8c (size: 43190 (43 blocks), refcount:2, expire: 2)
0x7fa54ecf1b4c (size: 45238 (45 blocks), refcount:0, expire: 2)
0x7fa54ed70cec (size: 61622 (61 blocks), refcount:0, expire: 2)
0x7fa54ecdbcac (size: 42166 (42 blocks), refcount:1, expire: 2)
0x7fa54ec9736c (size: 44214 (44 blocks), refcount:2, expire: 2)
0x7fa54eca28ec (size: 46262 (46 blocks), refcount:2, expire: -2)
2017-11-21 14:01:26 -05:00
|
|
|
#include <types/cli.h>
|
2017-11-21 14:01:24 -05:00
|
|
|
#include <types/filters.h>
|
|
|
|
|
#include <types/proxy.h>
|
|
|
|
|
|
2017-10-30 06:15:51 -04:00
|
|
|
#include <proto/channel.h>
|
MEDIUM: cache: "show cache" on the cli
The cli command "show cache" displays the status of the cache, the first
displayed line is the shctx informations with how much blocks available
blocks it contains (blocks are 1k by default).
The next lines are the objects stored in the cache tree, the pointer,
the size of the object and how much blocks it uses, a refcount for the
number of users of the object, and the remaining expiration time (which
can be negative if expired)
Example:
$ echo "show cache" | socat - /run/haproxy.sock
0x7fa54e9ab03a: foobar (shctx:0x7fa54e9ab000, available blocks:3921)
0x7fa54ed65b8c (size: 43190 (43 blocks), refcount:2, expire: 2)
0x7fa54ecf1b4c (size: 45238 (45 blocks), refcount:0, expire: 2)
0x7fa54ed70cec (size: 61622 (61 blocks), refcount:0, expire: 2)
0x7fa54ecdbcac (size: 42166 (42 blocks), refcount:1, expire: 2)
0x7fa54ec9736c (size: 44214 (44 blocks), refcount:2, expire: 2)
0x7fa54eca28ec (size: 46262 (46 blocks), refcount:2, expire: -2)
2017-11-21 14:01:26 -05:00
|
|
|
#include <proto/cli.h>
|
2017-10-30 06:15:51 -04:00
|
|
|
#include <proto/proxy.h>
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
#include <proto/http_htx.h>
|
2017-10-30 06:15:51 -04:00
|
|
|
#include <proto/filters.h>
|
2018-10-02 10:43:32 -04:00
|
|
|
#include <proto/http_rules.h>
|
2019-07-16 08:54:53 -04:00
|
|
|
#include <proto/http_ana.h>
|
2017-10-30 06:15:51 -04:00
|
|
|
#include <proto/log.h>
|
|
|
|
|
#include <proto/stream.h>
|
|
|
|
|
#include <proto/stream_interface.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <common/cfgparse.h>
|
2020-05-27 10:10:29 -04:00
|
|
|
#include <haproxy/hash.h>
|
2020-06-03 02:44:35 -04:00
|
|
|
#include <haproxy/htx.h>
|
2020-06-02 10:48:09 -04:00
|
|
|
#include <haproxy/net_helper.h>
|
2017-10-30 06:15:51 -04:00
|
|
|
|
BUG/MAJOR: compression/cache: Make it really works with these both filters
Caching the response with the compression enabled was totally broken. To fix the
problem, the compression must be done after caching the response. Otherwise it
needs to change the cache to store compressed and uncompressed objects for the
same ressource. So, because it is not possible for now, it is forbidden to
declare the compression filter before the cache one. To ease the configuration,
both can be implicitly declared (without "filter" keyword). The compression will
automatically be inserted after the cache.
Then, to make it works this way, the compression filter has been slighly
modified. Now, the response headers are updated after http-response rules
evaluations, instead of before. So, if the response contains a "Content-length"
header, it will be kept with the response stored in the cache. So this cached
response will be able to be served to clients not supporting the compression at
all.
2018-12-15 16:32:02 -05:00
|
|
|
#define CACHE_FLT_F_IMPLICIT_DECL 0x00000001 /* The cache filtre was implicitly declared (ie without
|
2018-12-11 03:18:27 -05:00
|
|
|
* the filter keyword) */
|
2018-12-11 02:57:45 -05:00
|
|
|
|
2018-12-07 11:39:53 -05:00
|
|
|
const char *cache_store_flt_id = "cache store filter";
|
2017-10-30 06:15:51 -04:00
|
|
|
|
2019-03-29 13:26:52 -04:00
|
|
|
extern struct applet http_cache_applet;
|
2017-10-30 06:15:51 -04:00
|
|
|
|
|
|
|
|
struct flt_ops cache_ops;
|
|
|
|
|
|
|
|
|
|
struct cache {
|
2017-11-26 02:54:31 -05:00
|
|
|
struct list list; /* cache linked list */
|
2017-10-30 06:15:51 -04:00
|
|
|
struct eb_root entries; /* head of cache entries based on keys */
|
2017-11-26 02:54:31 -05:00
|
|
|
unsigned int maxage; /* max-age */
|
|
|
|
|
unsigned int maxblocks;
|
2018-10-25 14:29:31 -04:00
|
|
|
unsigned int maxobjsz; /* max-object-size (in bytes) */
|
2017-11-26 02:54:31 -05:00
|
|
|
char id[33]; /* cache name */
|
2017-10-30 06:15:51 -04:00
|
|
|
};
|
|
|
|
|
|
2018-12-07 11:34:39 -05:00
|
|
|
/* cache config for filters */
|
|
|
|
|
struct cache_flt_conf {
|
|
|
|
|
union {
|
|
|
|
|
struct cache *cache; /* cache used by the filter */
|
|
|
|
|
char *name; /* cache name used during conf parsing */
|
|
|
|
|
} c;
|
|
|
|
|
unsigned int flags; /* CACHE_FLT_F_* */
|
|
|
|
|
};
|
|
|
|
|
|
2017-10-30 06:15:51 -04:00
|
|
|
/*
|
|
|
|
|
* cache ctx for filters
|
|
|
|
|
*/
|
|
|
|
|
struct cache_st {
|
|
|
|
|
struct shared_block *first_block;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct cache_entry {
|
|
|
|
|
unsigned int latest_validation; /* latest validation date */
|
|
|
|
|
unsigned int expire; /* expiration date */
|
2018-10-26 08:29:22 -04:00
|
|
|
unsigned int age; /* Origin server "Age" header value */
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2017-10-30 06:15:51 -04:00
|
|
|
struct eb32_node eb; /* ebtree node used to hold the cache object */
|
2017-11-23 13:43:17 -05:00
|
|
|
char hash[20];
|
2017-10-30 06:15:51 -04:00
|
|
|
unsigned char data[0];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define CACHE_BLOCKSIZE 1024
|
2018-11-11 08:00:28 -05:00
|
|
|
#define CACHE_ENTRY_MAX_AGE 2147483648U
|
2017-10-30 06:15:51 -04:00
|
|
|
|
|
|
|
|
static struct list caches = LIST_HEAD_INIT(caches);
|
2019-08-28 09:22:49 -04:00
|
|
|
static struct list caches_config = LIST_HEAD_INIT(caches_config); /* cache config to init */
|
2017-10-30 06:15:51 -04:00
|
|
|
static struct cache *tmp_cache_config = NULL;
|
|
|
|
|
|
2018-11-26 05:58:30 -05:00
|
|
|
DECLARE_STATIC_POOL(pool_head_cache_st, "cache_st", sizeof(struct cache_st));
|
|
|
|
|
|
2017-11-23 13:43:17 -05:00
|
|
|
struct cache_entry *entry_exist(struct cache *cache, char *hash)
|
2017-10-31 09:33:34 -04:00
|
|
|
{
|
|
|
|
|
struct eb32_node *node;
|
|
|
|
|
struct cache_entry *entry;
|
|
|
|
|
|
2020-02-25 03:35:07 -05:00
|
|
|
node = eb32_lookup(&cache->entries, read_u32(hash));
|
2017-10-31 09:33:34 -04:00
|
|
|
if (!node)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
entry = eb32_entry(node, struct cache_entry, eb);
|
2017-11-23 13:43:17 -05:00
|
|
|
|
|
|
|
|
/* if that's not the right node */
|
|
|
|
|
if (memcmp(entry->hash, hash, sizeof(entry->hash)))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2017-11-21 14:01:27 -05:00
|
|
|
if (entry->expire > now.tv_sec) {
|
2017-10-31 09:33:34 -04:00
|
|
|
return entry;
|
2017-11-21 14:01:27 -05:00
|
|
|
} else {
|
2017-10-31 09:33:34 -04:00
|
|
|
eb32_delete(node);
|
2017-11-21 14:01:27 -05:00
|
|
|
entry->eb.key = 0;
|
|
|
|
|
}
|
2017-10-31 09:33:34 -04:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline struct shared_context *shctx_ptr(struct cache *cache)
|
|
|
|
|
{
|
|
|
|
|
return (struct shared_context *)((unsigned char *)cache - ((struct shared_context *)NULL)->data);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-31 15:43:01 -04:00
|
|
|
static inline struct shared_block *block_ptr(struct cache_entry *entry)
|
|
|
|
|
{
|
|
|
|
|
return (struct shared_block *)((unsigned char *)entry - ((struct shared_block *)NULL)->data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-10-30 06:15:51 -04:00
|
|
|
static int
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
cache_store_init(struct proxy *px, struct flt_conf *fconf)
|
2017-10-30 06:15:51 -04:00
|
|
|
{
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
fconf->flags |= FLT_CFG_FL_HTX;
|
2017-10-30 06:15:51 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-07 11:34:39 -05:00
|
|
|
static void
|
|
|
|
|
cache_store_deinit(struct proxy *px, struct flt_conf *fconf)
|
|
|
|
|
{
|
|
|
|
|
struct cache_flt_conf *cconf = fconf->conf;
|
|
|
|
|
|
|
|
|
|
free(cconf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
cache_store_check(struct proxy *px, struct flt_conf *fconf)
|
|
|
|
|
{
|
|
|
|
|
struct cache_flt_conf *cconf = fconf->conf;
|
2018-12-11 02:57:45 -05:00
|
|
|
struct flt_conf *f;
|
2018-12-07 11:34:39 -05:00
|
|
|
struct cache *cache;
|
BUG/MAJOR: compression/cache: Make it really works with these both filters
Caching the response with the compression enabled was totally broken. To fix the
problem, the compression must be done after caching the response. Otherwise it
needs to change the cache to store compressed and uncompressed objects for the
same ressource. So, because it is not possible for now, it is forbidden to
declare the compression filter before the cache one. To ease the configuration,
both can be implicitly declared (without "filter" keyword). The compression will
automatically be inserted after the cache.
Then, to make it works this way, the compression filter has been slighly
modified. Now, the response headers are updated after http-response rules
evaluations, instead of before. So, if the response contains a "Content-length"
header, it will be kept with the response stored in the cache. So this cached
response will be able to be served to clients not supporting the compression at
all.
2018-12-15 16:32:02 -05:00
|
|
|
int comp = 0;
|
2018-12-07 11:34:39 -05:00
|
|
|
|
2019-08-28 09:22:49 -04:00
|
|
|
/* Find the cache corresponding to the name in the filter config. The
|
|
|
|
|
* cache will not be referenced now in the filter config because it is
|
|
|
|
|
* not fully allocated. This step will be performed during the cache
|
|
|
|
|
* post_check.
|
|
|
|
|
*/
|
|
|
|
|
list_for_each_entry(cache, &caches_config, list) {
|
|
|
|
|
if (!strcmp(cache->id, cconf->c.name))
|
2018-12-07 11:34:39 -05:00
|
|
|
goto found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ha_alert("config: %s '%s': unable to find the cache '%s' referenced by the filter 'cache'.\n",
|
|
|
|
|
proxy_type_str(px), px->id, (char *)cconf->c.name);
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
found:
|
2018-12-11 02:57:45 -05:00
|
|
|
/* Here <cache> points on the cache the filter must use and <cconf>
|
|
|
|
|
* points on the cache filter configuration. */
|
|
|
|
|
|
|
|
|
|
/* Check all filters for proxy <px> to know if the compression is
|
BUG/MAJOR: compression/cache: Make it really works with these both filters
Caching the response with the compression enabled was totally broken. To fix the
problem, the compression must be done after caching the response. Otherwise it
needs to change the cache to store compressed and uncompressed objects for the
same ressource. So, because it is not possible for now, it is forbidden to
declare the compression filter before the cache one. To ease the configuration,
both can be implicitly declared (without "filter" keyword). The compression will
automatically be inserted after the cache.
Then, to make it works this way, the compression filter has been slighly
modified. Now, the response headers are updated after http-response rules
evaluations, instead of before. So, if the response contains a "Content-length"
header, it will be kept with the response stored in the cache. So this cached
response will be able to be served to clients not supporting the compression at
all.
2018-12-15 16:32:02 -05:00
|
|
|
* enabled and if it is after the cache. When the compression is before
|
|
|
|
|
* the cache, an error is returned. Also check if the cache filter must
|
|
|
|
|
* be explicitly declaired or not. */
|
2018-12-11 02:57:45 -05:00
|
|
|
list_for_each_entry(f, &px->filter_configs, list) {
|
|
|
|
|
if (f == fconf) {
|
BUG/MAJOR: compression/cache: Make it really works with these both filters
Caching the response with the compression enabled was totally broken. To fix the
problem, the compression must be done after caching the response. Otherwise it
needs to change the cache to store compressed and uncompressed objects for the
same ressource. So, because it is not possible for now, it is forbidden to
declare the compression filter before the cache one. To ease the configuration,
both can be implicitly declared (without "filter" keyword). The compression will
automatically be inserted after the cache.
Then, to make it works this way, the compression filter has been slighly
modified. Now, the response headers are updated after http-response rules
evaluations, instead of before. So, if the response contains a "Content-length"
header, it will be kept with the response stored in the cache. So this cached
response will be able to be served to clients not supporting the compression at
all.
2018-12-15 16:32:02 -05:00
|
|
|
/* The compression filter must be evaluated after the cache. */
|
|
|
|
|
if (comp) {
|
|
|
|
|
ha_alert("config: %s '%s': unable to enable the compression filter before "
|
|
|
|
|
"the cache '%s'.\n", proxy_type_str(px), px->id, cache->id);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2018-12-11 03:18:27 -05:00
|
|
|
}
|
2019-07-15 09:08:25 -04:00
|
|
|
else if (f->id == http_comp_flt_id)
|
BUG/MAJOR: compression/cache: Make it really works with these both filters
Caching the response with the compression enabled was totally broken. To fix the
problem, the compression must be done after caching the response. Otherwise it
needs to change the cache to store compressed and uncompressed objects for the
same ressource. So, because it is not possible for now, it is forbidden to
declare the compression filter before the cache one. To ease the configuration,
both can be implicitly declared (without "filter" keyword). The compression will
automatically be inserted after the cache.
Then, to make it works this way, the compression filter has been slighly
modified. Now, the response headers are updated after http-response rules
evaluations, instead of before. So, if the response contains a "Content-length"
header, it will be kept with the response stored in the cache. So this cached
response will be able to be served to clients not supporting the compression at
all.
2018-12-15 16:32:02 -05:00
|
|
|
comp = 1;
|
2019-08-11 17:11:03 -04:00
|
|
|
else if (f->id == fcgi_flt_id)
|
|
|
|
|
continue;
|
BUG/MAJOR: compression/cache: Make it really works with these both filters
Caching the response with the compression enabled was totally broken. To fix the
problem, the compression must be done after caching the response. Otherwise it
needs to change the cache to store compressed and uncompressed objects for the
same ressource. So, because it is not possible for now, it is forbidden to
declare the compression filter before the cache one. To ease the configuration,
both can be implicitly declared (without "filter" keyword). The compression will
automatically be inserted after the cache.
Then, to make it works this way, the compression filter has been slighly
modified. Now, the response headers are updated after http-response rules
evaluations, instead of before. So, if the response contains a "Content-length"
header, it will be kept with the response stored in the cache. So this cached
response will be able to be served to clients not supporting the compression at
all.
2018-12-15 16:32:02 -05:00
|
|
|
else if ((f->id != fconf->id) && (cconf->flags & CACHE_FLT_F_IMPLICIT_DECL)) {
|
|
|
|
|
/* Implicit declaration is only allowed with the
|
2019-08-11 17:11:03 -04:00
|
|
|
* compression and fcgi. For other filters, an implicit
|
BUG/MAJOR: compression/cache: Make it really works with these both filters
Caching the response with the compression enabled was totally broken. To fix the
problem, the compression must be done after caching the response. Otherwise it
needs to change the cache to store compressed and uncompressed objects for the
same ressource. So, because it is not possible for now, it is forbidden to
declare the compression filter before the cache one. To ease the configuration,
both can be implicitly declared (without "filter" keyword). The compression will
automatically be inserted after the cache.
Then, to make it works this way, the compression filter has been slighly
modified. Now, the response headers are updated after http-response rules
evaluations, instead of before. So, if the response contains a "Content-length"
header, it will be kept with the response stored in the cache. So this cached
response will be able to be served to clients not supporting the compression at
all.
2018-12-15 16:32:02 -05:00
|
|
|
* declaration is required. */
|
|
|
|
|
ha_alert("config: %s '%s': require an explicit filter declaration "
|
|
|
|
|
"to use the cache '%s'.\n", proxy_type_str(px), px->id, cache->id);
|
|
|
|
|
return 1;
|
2018-12-11 02:57:45 -05:00
|
|
|
}
|
BUG/MAJOR: compression/cache: Make it really works with these both filters
Caching the response with the compression enabled was totally broken. To fix the
problem, the compression must be done after caching the response. Otherwise it
needs to change the cache to store compressed and uncompressed objects for the
same ressource. So, because it is not possible for now, it is forbidden to
declare the compression filter before the cache one. To ease the configuration,
both can be implicitly declared (without "filter" keyword). The compression will
automatically be inserted after the cache.
Then, to make it works this way, the compression filter has been slighly
modified. Now, the response headers are updated after http-response rules
evaluations, instead of before. So, if the response contains a "Content-length"
header, it will be kept with the response stored in the cache. So this cached
response will be able to be served to clients not supporting the compression at
all.
2018-12-15 16:32:02 -05:00
|
|
|
|
2018-12-11 02:57:45 -05:00
|
|
|
}
|
2018-12-07 11:34:39 -05:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-31 09:33:34 -04:00
|
|
|
static int
|
2020-03-06 08:52:06 -05:00
|
|
|
cache_store_strm_init(struct stream *s, struct filter *filter)
|
2017-10-31 09:33:34 -04:00
|
|
|
{
|
2020-03-06 08:52:06 -05:00
|
|
|
struct cache_st *st;
|
2017-10-31 09:33:34 -04:00
|
|
|
|
2020-03-06 08:52:06 -05:00
|
|
|
st = pool_alloc_dirty(pool_head_cache_st);
|
|
|
|
|
if (st == NULL)
|
|
|
|
|
return -1;
|
2017-10-31 09:33:34 -04:00
|
|
|
|
2020-03-06 08:52:06 -05:00
|
|
|
st->first_block = NULL;
|
|
|
|
|
filter->ctx = st;
|
2017-10-31 09:33:34 -04:00
|
|
|
|
2020-03-06 08:52:06 -05:00
|
|
|
/* Register post-analyzer on AN_RES_WAIT_HTTP */
|
|
|
|
|
filter->post_analyzers |= AN_RES_WAIT_HTTP;
|
2017-10-31 09:33:34 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-06 08:52:06 -05:00
|
|
|
static void
|
|
|
|
|
cache_store_strm_deinit(struct stream *s, struct filter *filter)
|
2017-11-24 08:33:54 -05:00
|
|
|
{
|
|
|
|
|
struct cache_st *st = filter->ctx;
|
2018-12-07 11:34:39 -05:00
|
|
|
struct cache_flt_conf *cconf = FLT_CONF(filter);
|
|
|
|
|
struct cache *cache = cconf->c.cache;
|
2017-11-24 08:33:54 -05:00
|
|
|
struct shared_context *shctx = shctx_ptr(cache);
|
|
|
|
|
|
|
|
|
|
/* Everything should be released in the http_end filter, but we need to do it
|
|
|
|
|
* there too, in case of errors */
|
|
|
|
|
if (st && st->first_block) {
|
|
|
|
|
shctx_lock(shctx);
|
|
|
|
|
shctx_row_dec_hot(shctx, st->first_block);
|
|
|
|
|
shctx_unlock(shctx);
|
|
|
|
|
}
|
|
|
|
|
if (st) {
|
2017-11-24 11:34:44 -05:00
|
|
|
pool_free(pool_head_cache_st, st);
|
2017-11-24 08:33:54 -05:00
|
|
|
filter->ctx = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-07 10:12:07 -05:00
|
|
|
static int
|
|
|
|
|
cache_store_post_analyze(struct stream *s, struct filter *filter, struct channel *chn,
|
|
|
|
|
unsigned an_bit)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = s->txn;
|
|
|
|
|
struct http_msg *msg = &txn->rsp;
|
|
|
|
|
struct cache_st *st = filter->ctx;
|
|
|
|
|
|
|
|
|
|
if (an_bit != AN_RES_WAIT_HTTP)
|
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
|
|
/* Here we need to check if any compression filter precedes the cache
|
|
|
|
|
* filter. This is only possible when the compression is configured in
|
|
|
|
|
* the frontend while the cache filter is configured on the
|
|
|
|
|
* backend. This case cannot be detected during HAProxy startup. So in
|
|
|
|
|
* such cases, the cache is disabled.
|
|
|
|
|
*/
|
|
|
|
|
if (st && (msg->flags & HTTP_MSGF_COMPRESSING)) {
|
|
|
|
|
pool_free(pool_head_cache_st, st);
|
|
|
|
|
filter->ctx = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2017-11-24 08:33:54 -05:00
|
|
|
|
2017-10-31 09:33:34 -04:00
|
|
|
static int
|
|
|
|
|
cache_store_http_headers(struct stream *s, struct filter *filter, struct http_msg *msg)
|
|
|
|
|
{
|
|
|
|
|
struct cache_st *st = filter->ctx;
|
|
|
|
|
|
|
|
|
|
if (!(msg->chn->flags & CF_ISRESP) || !st)
|
|
|
|
|
return 1;
|
|
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
if (st->first_block)
|
2018-12-06 15:59:39 -05:00
|
|
|
register_data_filter(s, msg->chn, filter);
|
2017-10-31 09:33:34 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-22 12:01:48 -04:00
|
|
|
static inline void disable_cache_entry(struct cache_st *st,
|
|
|
|
|
struct filter *filter, struct shared_context *shctx)
|
|
|
|
|
{
|
|
|
|
|
struct cache_entry *object;
|
|
|
|
|
|
|
|
|
|
object = (struct cache_entry *)st->first_block->data;
|
|
|
|
|
filter->ctx = NULL; /* disable cache */
|
|
|
|
|
shctx_lock(shctx);
|
|
|
|
|
shctx_row_dec_hot(shctx, st->first_block);
|
|
|
|
|
object->eb.key = 0;
|
|
|
|
|
shctx_unlock(shctx);
|
|
|
|
|
pool_free(pool_head_cache_st, st);
|
|
|
|
|
}
|
|
|
|
|
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
static int
|
|
|
|
|
cache_store_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
|
|
|
|
|
unsigned int offset, unsigned int len)
|
|
|
|
|
{
|
2018-12-07 11:34:39 -05:00
|
|
|
struct cache_flt_conf *cconf = FLT_CONF(filter);
|
|
|
|
|
struct shared_context *shctx = shctx_ptr(cconf->c.cache);
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
struct cache_st *st = filter->ctx;
|
|
|
|
|
struct htx *htx = htxbuf(&msg->chn->buf);
|
|
|
|
|
struct htx_blk *blk;
|
2019-06-03 16:19:18 -04:00
|
|
|
struct shared_block *fb;
|
2020-03-02 10:19:50 -05:00
|
|
|
struct htx_ret htxret;
|
2019-06-03 16:19:18 -04:00
|
|
|
unsigned int orig_len, to_forward;
|
|
|
|
|
int ret;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
|
|
|
|
if (!len)
|
|
|
|
|
return len;
|
|
|
|
|
|
|
|
|
|
if (!st->first_block) {
|
|
|
|
|
unregister_data_filter(s, msg->chn, filter);
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
chunk_reset(&trash);
|
|
|
|
|
orig_len = len;
|
|
|
|
|
to_forward = 0;
|
2020-03-02 10:19:50 -05:00
|
|
|
|
|
|
|
|
htxret = htx_find_offset(htx, offset);
|
|
|
|
|
blk = htxret.blk;
|
|
|
|
|
offset = htxret.ret;
|
|
|
|
|
for (; blk && len; blk = htx_get_next_blk(htx, blk)) {
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
enum htx_blk_type type = htx_get_blk_type(blk);
|
2019-06-03 16:19:18 -04:00
|
|
|
uint32_t info, sz = htx_get_blksz(blk);
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
struct ist v;
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case HTX_BLK_UNUSED:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case HTX_BLK_DATA:
|
|
|
|
|
v = htx_get_blk_value(htx, blk);
|
|
|
|
|
v.ptr += offset;
|
|
|
|
|
v.len -= offset;
|
|
|
|
|
if (v.len > len)
|
|
|
|
|
v.len = len;
|
|
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
info = (type << 28) + v.len;
|
|
|
|
|
chunk_memcat(&trash, (char *)&info, sizeof(info));
|
|
|
|
|
chunk_memcat(&trash, v.ptr, v.len);
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
to_forward += v.len;
|
|
|
|
|
len -= v.len;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2019-06-03 16:19:18 -04:00
|
|
|
/* Here offset must always be 0 because only
|
|
|
|
|
* DATA blocks can be partially transferred. */
|
|
|
|
|
if (offset)
|
|
|
|
|
goto no_cache;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
if (sz > len)
|
2019-06-03 16:19:18 -04:00
|
|
|
goto end;
|
|
|
|
|
|
|
|
|
|
chunk_memcat(&trash, (char *)&blk->info, sizeof(blk->info));
|
|
|
|
|
chunk_memcat(&trash, htx_get_blk_ptr(htx, blk), sz);
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
to_forward += sz;
|
|
|
|
|
len -= sz;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
offset = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
end:
|
|
|
|
|
shctx_lock(shctx);
|
|
|
|
|
fb = shctx_row_reserve_hot(shctx, st->first_block, trash.data);
|
|
|
|
|
if (!fb) {
|
|
|
|
|
shctx_unlock(shctx);
|
|
|
|
|
goto no_cache;
|
|
|
|
|
}
|
|
|
|
|
shctx_unlock(shctx);
|
|
|
|
|
|
|
|
|
|
ret = shctx_row_data_append(shctx, st->first_block, st->first_block->last_append,
|
|
|
|
|
(unsigned char *)b_head(&trash), b_data(&trash));
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
goto no_cache;
|
|
|
|
|
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
return to_forward;
|
|
|
|
|
|
|
|
|
|
no_cache:
|
|
|
|
|
disable_cache_entry(st, filter, shctx);
|
|
|
|
|
unregister_data_filter(s, msg->chn, filter);
|
2019-06-03 16:19:18 -04:00
|
|
|
return orig_len;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
}
|
|
|
|
|
|
2017-10-31 09:33:34 -04:00
|
|
|
static int
|
|
|
|
|
cache_store_http_end(struct stream *s, struct filter *filter,
|
|
|
|
|
struct http_msg *msg)
|
|
|
|
|
{
|
|
|
|
|
struct cache_st *st = filter->ctx;
|
2018-12-07 11:34:39 -05:00
|
|
|
struct cache_flt_conf *cconf = FLT_CONF(filter);
|
|
|
|
|
struct cache *cache = cconf->c.cache;
|
2017-10-31 09:33:34 -04:00
|
|
|
struct shared_context *shctx = shctx_ptr(cache);
|
|
|
|
|
struct cache_entry *object;
|
|
|
|
|
|
|
|
|
|
if (!(msg->chn->flags & CF_ISRESP))
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
if (st && st->first_block) {
|
|
|
|
|
|
|
|
|
|
object = (struct cache_entry *)st->first_block->data;
|
|
|
|
|
|
|
|
|
|
/* does not need to test if the insertion worked, if it
|
|
|
|
|
* doesn't, the blocks will be reused anyway */
|
|
|
|
|
|
|
|
|
|
shctx_lock(shctx);
|
2017-11-21 14:01:27 -05:00
|
|
|
if (eb32_insert(&cache->entries, &object->eb) != &object->eb) {
|
|
|
|
|
object->eb.key = 0;
|
|
|
|
|
}
|
2017-10-31 09:33:34 -04:00
|
|
|
/* remove from the hotlist */
|
|
|
|
|
shctx_row_dec_hot(shctx, st->first_block);
|
|
|
|
|
shctx_unlock(shctx);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if (st) {
|
2017-11-24 11:34:44 -05:00
|
|
|
pool_free(pool_head_cache_st, st);
|
2017-10-31 09:33:34 -04:00
|
|
|
filter->ctx = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This intends to be used when checking HTTP headers for some
|
|
|
|
|
* word=value directive. Return a pointer to the first character of value, if
|
|
|
|
|
* the word was not found or if there wasn't any value assigned ot it return NULL
|
|
|
|
|
*/
|
|
|
|
|
char *directive_value(const char *sample, int slen, const char *word, int wlen)
|
|
|
|
|
{
|
|
|
|
|
int st = 0;
|
|
|
|
|
|
|
|
|
|
if (slen < wlen)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
while (wlen) {
|
|
|
|
|
char c = *sample ^ *word;
|
|
|
|
|
if (c && c != ('A' ^ 'a'))
|
|
|
|
|
return NULL;
|
|
|
|
|
sample++;
|
|
|
|
|
word++;
|
|
|
|
|
slen--;
|
|
|
|
|
wlen--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (slen) {
|
|
|
|
|
if (st == 0) {
|
|
|
|
|
if (*sample != '=')
|
|
|
|
|
return NULL;
|
|
|
|
|
sample++;
|
|
|
|
|
slen--;
|
|
|
|
|
st = 1;
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
return (char *)sample;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Return the maxage in seconds of an HTTP response.
|
|
|
|
|
* Compute the maxage using either:
|
|
|
|
|
* - the assigned max-age of the cache
|
|
|
|
|
* - the s-maxage directive
|
|
|
|
|
* - the max-age directive
|
|
|
|
|
* - (Expires - Data) headers
|
|
|
|
|
* - the default-max-age of the cache
|
|
|
|
|
*
|
|
|
|
|
*/
|
2017-11-24 12:53:43 -05:00
|
|
|
int http_calc_maxage(struct stream *s, struct cache *cache)
|
2017-10-31 09:33:34 -04:00
|
|
|
{
|
2019-07-15 15:01:29 -04:00
|
|
|
struct htx *htx = htxbuf(&s->res.buf);
|
|
|
|
|
struct http_hdr_ctx ctx = { .blk = NULL };
|
2017-10-31 09:33:34 -04:00
|
|
|
int smaxage = -1;
|
|
|
|
|
int maxage = -1;
|
|
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
while (http_find_header(htx, ist("cache-control"), &ctx, 0)) {
|
|
|
|
|
char *value;
|
2017-10-31 09:33:34 -04:00
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
value = directive_value(ctx.value.ptr, ctx.value.len, "s-maxage", 8);
|
|
|
|
|
if (value) {
|
|
|
|
|
struct buffer *chk = get_trash_chunk();
|
2017-10-31 09:33:34 -04:00
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
chunk_strncat(chk, value, ctx.value.len - 8 + 1);
|
|
|
|
|
chunk_strncat(chk, "", 1);
|
|
|
|
|
maxage = atoi(chk->area);
|
2017-10-31 09:33:34 -04:00
|
|
|
}
|
2019-07-15 14:49:46 -04:00
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
value = directive_value(ctx.value.ptr, ctx.value.len, "max-age", 7);
|
|
|
|
|
if (value) {
|
|
|
|
|
struct buffer *chk = get_trash_chunk();
|
2019-07-15 14:49:46 -04:00
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
chunk_strncat(chk, value, ctx.value.len - 7 + 1);
|
|
|
|
|
chunk_strncat(chk, "", 1);
|
|
|
|
|
smaxage = atoi(chk->area);
|
2017-10-31 09:33:34 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TODO: Expires - Data */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (smaxage > 0)
|
2017-11-24 12:53:43 -05:00
|
|
|
return MIN(smaxage, cache->maxage);
|
2017-10-31 09:33:34 -04:00
|
|
|
|
|
|
|
|
if (maxage > 0)
|
2017-11-24 12:53:43 -05:00
|
|
|
return MIN(maxage, cache->maxage);
|
2017-10-31 09:33:34 -04:00
|
|
|
|
2017-11-24 12:53:43 -05:00
|
|
|
return cache->maxage;
|
2017-10-31 09:33:34 -04:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-11-20 13:13:12 -05:00
|
|
|
static void cache_free_blocks(struct shared_block *first, struct shared_block *block)
|
|
|
|
|
{
|
BUG/MAJOR: cache: fix random crashes caused by incorrect delete() on non-first blocks
Several segfaults were reported in the cache, each time in eb_delete()
called from cache_free_blocks() itself called from shctx_row_reserve_hot().
Each time the tree node was corrupted with random cached data (often JS or
HTML contents).
The problem comes from an incompatibility between the cache's expectations
and the recycling algorithm used in the shctx. The shctx allocates and
releases a chain of blocks at once. And when it needs to allocate N blocks
from the avail list while a chain of M>N is found, it picks the first N
from the list, moves them to the hot list, and marks all remaining M-N
blocks as isolated blocks (chains of 1).
For each such released block, the shctx->free_block() callback is used
and passed a pointer to the first and current block of the chain. For
the cache, it's cache_free_blocks(). What this function does is check
that the current block is the first one, and in this case delete the
object from the tree and mark it as not in tree by setting key to zero.
The problem this causes is that the tail blocks when M>N become first
blocks for the next call to shctx_row_reserve_hot(), these ones will
be passed to cache_free_blocks() as list heads, and will be sent to
eb_delete() despite containing only cached data.
The simplest solution for now is to mark each block as holding no cache
object by setting key to zero all the time. It keeps the principle used
elsewhere in the code. The SSL code is not subject to this problem
because it relies on the block's len not being null, which happens
immediately after a block was released. It was uncertain however whether
this method is suitable for the cache. It is not critical though since
this code is going to change soon in 1.9 to dynamically allocate only
the number of required blocks.
This fix must be backported to 1.8. Thanks to Thierry for providing
exploitable cores.
2018-04-04 14:17:03 -04:00
|
|
|
struct cache_entry *object = (struct cache_entry *)block->data;
|
|
|
|
|
|
|
|
|
|
if (first == block && object->eb.key)
|
|
|
|
|
eb32_delete(&object->eb);
|
|
|
|
|
object->eb.key = 0;
|
2017-11-20 13:13:12 -05:00
|
|
|
}
|
|
|
|
|
|
2017-10-30 06:15:51 -04:00
|
|
|
/*
|
2020-04-02 06:25:26 -04:00
|
|
|
* This function will store the headers of the response in a buffer and then
|
2017-10-30 06:15:51 -04:00
|
|
|
* register a filter to store the data
|
|
|
|
|
*/
|
|
|
|
|
enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
|
2019-06-03 16:19:18 -04:00
|
|
|
struct session *sess, struct stream *s, int flags)
|
2017-10-30 06:15:51 -04:00
|
|
|
{
|
2018-10-26 08:29:22 -04:00
|
|
|
unsigned int age;
|
|
|
|
|
long long hdr_age;
|
2017-10-31 09:33:34 -04:00
|
|
|
struct http_txn *txn = s->txn;
|
|
|
|
|
struct http_msg *msg = &txn->rsp;
|
|
|
|
|
struct filter *filter;
|
|
|
|
|
struct shared_block *first = NULL;
|
2018-12-07 11:34:39 -05:00
|
|
|
struct cache_flt_conf *cconf = rule->arg.act.p[0];
|
|
|
|
|
struct shared_context *shctx = shctx_ptr(cconf->c.cache);
|
2019-01-07 10:12:07 -05:00
|
|
|
struct cache_st *cache_ctx = NULL;
|
|
|
|
|
struct cache_entry *object, *old;
|
2020-02-25 03:35:07 -05:00
|
|
|
unsigned int key = read_u32(txn->cache_hash);
|
2019-07-15 15:01:29 -04:00
|
|
|
struct htx *htx;
|
|
|
|
|
struct http_hdr_ctx ctx;
|
2019-09-03 16:22:12 -04:00
|
|
|
size_t hdrs_len = 0;
|
2019-07-15 15:01:29 -04:00
|
|
|
int32_t pos;
|
2017-10-31 09:33:34 -04:00
|
|
|
|
|
|
|
|
/* Don't cache if the response came from a cache */
|
|
|
|
|
if ((obj_type(s->target) == OBJ_TYPE_APPLET) &&
|
|
|
|
|
s->target == &http_cache_applet.obj_type) {
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* cache only HTTP/1.1 */
|
|
|
|
|
if (!(txn->req.flags & HTTP_MSGF_VER_11))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2019-10-01 11:59:17 -04:00
|
|
|
/* cache only GET method */
|
|
|
|
|
if (txn->meth != HTTP_METH_GET)
|
2017-10-31 09:33:34 -04:00
|
|
|
goto out;
|
|
|
|
|
|
BUG/MAJOR: cache: fix confusion between zero and uninitialized cache key
The cache uses the first 32 bits of the uri's hash as the key to reference
the object in the cache. It makes a special case of the value zero to mean
that the object is not in the cache anymore. The problem is that when an
object hashes as zero, it's still inserted but the eb32_delete() call is
skipped, resulting in the object still being chained in the memory area
while the block has been reclaimed and used for something else. Then when
objects which were chained below it (techically any object since zero is
at the root) are deleted, the walk through the upper object may encounter
corrupted values where valid pointers were expected.
But while this should only happen statically once on 4 billion, the problem
gets worse when the cache-use conditions don't match the cache-store ones,
because cache-store runs with an uninitialized key, which can create objects
that will never be found by the lookup code, or worse, entries with a zero
key preventing eviction of the tree node and resulting in a crash. It's easy
to accidently end up on such a config because the request rules generally
can't be used to decide on the response :
http-request cache-use cache if { path_beg /images }
http-response cache-store cache
In this test, mixing traffic with /images/$RANDOM and /foo/$RANDOM will
result in random keys being inserted, some of them possibly being zero,
and crashes will quickly happen.
The fix consists in 1) always initializing the transaction's cache_hash
to zero, and 2) never storing a response for which the hash has not been
calculated, as indicated by the value zero.
It is worth noting that objects hashing as value zero will never be cached,
but given that there's only one chance among 4 billion that this happens,
this is totally harmless.
This fix must be backported to 1.9 and 1.8.
2019-01-11 13:38:25 -05:00
|
|
|
/* cache key was not computed */
|
|
|
|
|
if (!key)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2017-10-31 09:33:34 -04:00
|
|
|
/* cache only 200 status code */
|
|
|
|
|
if (txn->status != 200)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2019-01-07 10:12:07 -05:00
|
|
|
/* Find the corresponding filter instance for the current stream */
|
|
|
|
|
list_for_each_entry(filter, &s->strm_flt.filters, list) {
|
|
|
|
|
if (FLT_ID(filter) == cache_store_flt_id && FLT_CONF(filter) == cconf) {
|
|
|
|
|
/* No filter ctx, don't cache anything */
|
|
|
|
|
if (!filter->ctx)
|
|
|
|
|
goto out;
|
|
|
|
|
cache_ctx = filter->ctx;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* from there, cache_ctx is always defined */
|
2019-07-15 15:01:29 -04:00
|
|
|
htx = htxbuf(&s->res.buf);
|
2019-01-07 10:12:07 -05:00
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
/* Do not cache too big objects. */
|
|
|
|
|
if ((msg->flags & HTTP_MSGF_CNT_LEN) && shctx->max_obj_size > 0 &&
|
|
|
|
|
htx->data + htx->extra > shctx->max_obj_size)
|
|
|
|
|
goto out;
|
2017-10-31 09:33:34 -04:00
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
/* Does not manage Vary at the moment. We will need a secondary key later for that */
|
|
|
|
|
ctx.blk = NULL;
|
|
|
|
|
if (http_find_header(htx, ist("Vary"), &ctx, 0))
|
|
|
|
|
goto out;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-07-16 08:54:53 -04:00
|
|
|
http_check_response_for_cacheability(s, &s->res);
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
if (!(txn->flags & TX_CACHEABLE) || !(txn->flags & TX_CACHE_COOK))
|
|
|
|
|
goto out;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
age = 0;
|
|
|
|
|
ctx.blk = NULL;
|
|
|
|
|
if (http_find_header(htx, ist("Age"), &ctx, 0)) {
|
|
|
|
|
if (!strl2llrc(ctx.value.ptr, ctx.value.len, &hdr_age) && hdr_age > 0) {
|
|
|
|
|
if (unlikely(hdr_age > CACHE_ENTRY_MAX_AGE))
|
|
|
|
|
hdr_age = CACHE_ENTRY_MAX_AGE;
|
|
|
|
|
age = hdr_age;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
}
|
2019-07-15 15:01:29 -04:00
|
|
|
http_remove_header(htx, &ctx);
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
}
|
|
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
chunk_reset(&trash);
|
|
|
|
|
for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
|
|
|
|
|
struct htx_blk *blk = htx_get_blk(htx, pos);
|
|
|
|
|
enum htx_blk_type type = htx_get_blk_type(blk);
|
|
|
|
|
uint32_t sz = htx_get_blksz(blk);
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-09-03 16:22:12 -04:00
|
|
|
hdrs_len += sizeof(*blk) + sz;
|
2019-07-15 15:01:29 -04:00
|
|
|
chunk_memcat(&trash, (char *)&blk->info, sizeof(blk->info));
|
|
|
|
|
chunk_memcat(&trash, htx_get_blk_ptr(htx, blk), sz);
|
|
|
|
|
if (type == HTX_BLK_EOH)
|
|
|
|
|
break;
|
2018-10-26 08:29:22 -04:00
|
|
|
}
|
|
|
|
|
|
2019-09-03 16:22:12 -04:00
|
|
|
/* Do not cache objects if the headers are too big. */
|
|
|
|
|
if (hdrs_len > htx->size - global.tune.maxrewrite)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2017-10-31 09:33:34 -04:00
|
|
|
shctx_lock(shctx);
|
2019-07-15 15:01:29 -04:00
|
|
|
first = shctx_row_reserve_hot(shctx, NULL, sizeof(struct cache_entry) + trash.data);
|
2017-10-31 09:33:34 -04:00
|
|
|
if (!first) {
|
|
|
|
|
shctx_unlock(shctx);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
shctx_unlock(shctx);
|
|
|
|
|
|
2018-04-06 13:02:25 -04:00
|
|
|
/* the received memory is not initialized, we need at least to mark
|
|
|
|
|
* the object as not indexed yet.
|
|
|
|
|
*/
|
|
|
|
|
object = (struct cache_entry *)first->data;
|
|
|
|
|
object->eb.node.leaf_p = NULL;
|
|
|
|
|
object->eb.key = 0;
|
2018-10-26 08:29:22 -04:00
|
|
|
object->age = age;
|
2018-04-06 13:02:25 -04:00
|
|
|
|
2017-10-31 09:33:34 -04:00
|
|
|
/* reserve space for the cache_entry structure */
|
|
|
|
|
first->len = sizeof(struct cache_entry);
|
2018-10-22 12:01:48 -04:00
|
|
|
first->last_append = NULL;
|
2017-10-31 09:33:34 -04:00
|
|
|
/* cache the headers in a http action because it allows to chose what
|
|
|
|
|
* to cache, for example you might want to cache a response before
|
|
|
|
|
* modifying some HTTP headers, or on the contrary after modifying
|
|
|
|
|
* those headers.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* does not need to be locked because it's in the "hot" list,
|
|
|
|
|
* copy the headers */
|
2019-07-15 15:01:29 -04:00
|
|
|
if (shctx_row_data_append(shctx, first, NULL, (unsigned char *)trash.area, trash.data) < 0)
|
|
|
|
|
goto out;
|
2017-10-31 09:33:34 -04:00
|
|
|
|
|
|
|
|
/* register the buffer in the filter ctx for filling it with data*/
|
2019-01-07 10:12:07 -05:00
|
|
|
if (cache_ctx) {
|
|
|
|
|
cache_ctx->first_block = first;
|
2018-12-07 11:34:39 -05:00
|
|
|
|
BUG/MAJOR: cache: fix confusion between zero and uninitialized cache key
The cache uses the first 32 bits of the uri's hash as the key to reference
the object in the cache. It makes a special case of the value zero to mean
that the object is not in the cache anymore. The problem is that when an
object hashes as zero, it's still inserted but the eb32_delete() call is
skipped, resulting in the object still being chained in the memory area
while the block has been reclaimed and used for something else. Then when
objects which were chained below it (techically any object since zero is
at the root) are deleted, the walk through the upper object may encounter
corrupted values where valid pointers were expected.
But while this should only happen statically once on 4 billion, the problem
gets worse when the cache-use conditions don't match the cache-store ones,
because cache-store runs with an uninitialized key, which can create objects
that will never be found by the lookup code, or worse, entries with a zero
key preventing eviction of the tree node and resulting in a crash. It's easy
to accidently end up on such a config because the request rules generally
can't be used to decide on the response :
http-request cache-use cache if { path_beg /images }
http-response cache-store cache
In this test, mixing traffic with /images/$RANDOM and /foo/$RANDOM will
result in random keys being inserted, some of them possibly being zero,
and crashes will quickly happen.
The fix consists in 1) always initializing the transaction's cache_hash
to zero, and 2) never storing a response for which the hash has not been
calculated, as indicated by the value zero.
It is worth noting that objects hashing as value zero will never be cached,
but given that there's only one chance among 4 billion that this happens,
this is totally harmless.
This fix must be backported to 1.9 and 1.8.
2019-01-11 13:38:25 -05:00
|
|
|
object->eb.key = key;
|
|
|
|
|
|
2019-01-07 10:12:07 -05:00
|
|
|
memcpy(object->hash, txn->cache_hash, sizeof(object->hash));
|
|
|
|
|
/* Insert the node later on caching success */
|
2018-12-07 11:34:39 -05:00
|
|
|
|
2019-01-07 10:12:07 -05:00
|
|
|
shctx_lock(shctx);
|
2018-12-07 11:34:39 -05:00
|
|
|
|
2019-01-07 10:12:07 -05:00
|
|
|
old = entry_exist(cconf->c.cache, txn->cache_hash);
|
|
|
|
|
if (old) {
|
|
|
|
|
eb32_delete(&old->eb);
|
|
|
|
|
old->eb.key = 0;
|
2017-10-31 09:33:34 -04:00
|
|
|
}
|
2019-01-07 10:12:07 -05:00
|
|
|
shctx_unlock(shctx);
|
|
|
|
|
|
|
|
|
|
/* store latest value and expiration time */
|
|
|
|
|
object->latest_validation = now.tv_sec;
|
|
|
|
|
object->expire = now.tv_sec + http_calc_maxage(s, cconf->c.cache);
|
|
|
|
|
return ACT_RET_CONT;
|
2017-10-31 09:33:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
/* if does not cache */
|
|
|
|
|
if (first) {
|
|
|
|
|
shctx_lock(shctx);
|
2017-11-21 14:01:27 -05:00
|
|
|
first->len = 0;
|
|
|
|
|
object->eb.key = 0;
|
2017-10-31 09:33:34 -04:00
|
|
|
shctx_row_dec_hot(shctx, first);
|
|
|
|
|
shctx_unlock(shctx);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-30 06:15:51 -04:00
|
|
|
return ACT_RET_CONT;
|
|
|
|
|
}
|
|
|
|
|
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
#define HTX_CACHE_INIT 0 /* Initial state. */
|
|
|
|
|
#define HTX_CACHE_HEADER 1 /* Cache entry headers forwarding */
|
|
|
|
|
#define HTX_CACHE_DATA 2 /* Cache entry data forwarding */
|
2019-06-03 16:19:18 -04:00
|
|
|
#define HTX_CACHE_EOM 3 /* Cache entry completely forwarded. Finish the HTX message */
|
|
|
|
|
#define HTX_CACHE_END 4 /* Cache entry treatment terminated */
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2017-11-24 08:33:55 -05:00
|
|
|
static void http_cache_applet_release(struct appctx *appctx)
|
|
|
|
|
{
|
2018-12-07 11:34:39 -05:00
|
|
|
struct cache_flt_conf *cconf = appctx->rule->arg.act.p[0];
|
2017-11-24 08:33:55 -05:00
|
|
|
struct cache_entry *cache_ptr = appctx->ctx.cache.entry;
|
2018-12-07 11:34:39 -05:00
|
|
|
struct cache *cache = cconf->c.cache;
|
2017-11-24 08:33:55 -05:00
|
|
|
struct shared_block *first = block_ptr(cache_ptr);
|
|
|
|
|
|
|
|
|
|
shctx_lock(shctx_ptr(cache));
|
|
|
|
|
shctx_row_dec_hot(shctx_ptr(cache), first);
|
|
|
|
|
shctx_unlock(shctx_ptr(cache));
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
|
|
|
|
|
static unsigned int htx_cache_dump_blk(struct appctx *appctx, struct htx *htx, enum htx_blk_type type,
|
|
|
|
|
uint32_t info, struct shared_block *shblk, unsigned int offset)
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
{
|
2018-12-07 11:34:39 -05:00
|
|
|
struct cache_flt_conf *cconf = appctx->rule->arg.act.p[0];
|
|
|
|
|
struct shared_context *shctx = shctx_ptr(cconf->c.cache);
|
2019-06-03 16:19:18 -04:00
|
|
|
struct htx_blk *blk;
|
2019-09-03 16:11:52 -04:00
|
|
|
char *ptr;
|
2019-06-03 16:19:18 -04:00
|
|
|
unsigned int max, total;
|
|
|
|
|
uint32_t blksz;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
max = htx_get_max_blksz(htx, channel_htx_recv_max(si_ic(appctx->owner), htx));
|
|
|
|
|
if (!max)
|
|
|
|
|
return 0;
|
2019-06-03 04:41:26 -04:00
|
|
|
blksz = ((type == HTX_BLK_HDR || type == HTX_BLK_TLR)
|
2019-06-03 16:19:18 -04:00
|
|
|
? (info & 0xff) + ((info >> 8) & 0xfffff)
|
|
|
|
|
: info & 0xfffffff);
|
|
|
|
|
if (blksz > max)
|
|
|
|
|
return 0;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
blk = htx_add_blk(htx, type, blksz);
|
|
|
|
|
if (!blk)
|
|
|
|
|
return 0;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
blk->info = info;
|
|
|
|
|
total = 4;
|
2019-09-03 16:11:52 -04:00
|
|
|
ptr = htx_get_blk_ptr(htx, blk);
|
2019-06-03 16:19:18 -04:00
|
|
|
while (blksz) {
|
|
|
|
|
max = MIN(blksz, shctx->block_size - offset);
|
2019-09-03 16:11:52 -04:00
|
|
|
memcpy(ptr, (const char *)shblk->data + offset, max);
|
2019-06-03 16:19:18 -04:00
|
|
|
offset += max;
|
|
|
|
|
blksz -= max;
|
|
|
|
|
total += max;
|
2019-09-03 16:11:52 -04:00
|
|
|
ptr += max;
|
2019-06-03 16:19:18 -04:00
|
|
|
if (blksz || offset == shctx->block_size) {
|
|
|
|
|
shblk = LIST_NEXT(&shblk->list, typeof(shblk), list);
|
|
|
|
|
offset = 0;
|
|
|
|
|
}
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
}
|
2019-06-03 16:19:18 -04:00
|
|
|
appctx->ctx.cache.offset = offset;
|
|
|
|
|
appctx->ctx.cache.next = shblk;
|
|
|
|
|
appctx->ctx.cache.sent += total;
|
|
|
|
|
return total;
|
|
|
|
|
}
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
static unsigned int htx_cache_dump_data_blk(struct appctx *appctx, struct htx *htx,
|
|
|
|
|
uint32_t info, struct shared_block *shblk, unsigned int offset)
|
|
|
|
|
{
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
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;
|
|
|
|
|
uint32_t blksz;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
max = htx_get_max_blksz(htx, channel_htx_recv_max(si_ic(appctx->owner), htx));
|
|
|
|
|
if (!max)
|
|
|
|
|
return 0;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
rem_data = 0;
|
2019-06-11 03:58:09 -04:00
|
|
|
if (appctx->ctx.cache.rem_data) {
|
|
|
|
|
blksz = appctx->ctx.cache.rem_data;
|
2019-06-03 16:19:18 -04:00
|
|
|
total = 0;
|
2019-06-11 03:58:09 -04:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
blksz = (info & 0xfffffff);
|
|
|
|
|
total = 4;
|
|
|
|
|
}
|
|
|
|
|
if (blksz > max) {
|
2019-06-03 16:19:18 -04:00
|
|
|
rem_data = blksz - max;
|
|
|
|
|
blksz = max;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
}
|
|
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
while (blksz) {
|
|
|
|
|
size_t sz;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
max = MIN(blksz, shctx->block_size - offset);
|
|
|
|
|
sz = htx_add_data(htx, ist2(shblk->data + offset, max));
|
|
|
|
|
offset += sz;
|
|
|
|
|
blksz -= sz;
|
|
|
|
|
total += sz;
|
|
|
|
|
if (sz < max)
|
|
|
|
|
break;
|
|
|
|
|
if (blksz || offset == shctx->block_size) {
|
|
|
|
|
shblk = LIST_NEXT(&shblk->list, typeof(shblk), list);
|
|
|
|
|
offset = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appctx->ctx.cache.offset = offset;
|
|
|
|
|
appctx->ctx.cache.next = shblk;
|
|
|
|
|
appctx->ctx.cache.sent += total;
|
|
|
|
|
appctx->ctx.cache.rem_data = rem_data + blksz;
|
|
|
|
|
return total;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
}
|
|
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
static size_t htx_cache_dump_msg(struct appctx *appctx, struct htx *htx, unsigned int len,
|
|
|
|
|
enum htx_blk_type mark)
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
{
|
2018-12-07 11:34:39 -05:00
|
|
|
struct cache_flt_conf *cconf = appctx->rule->arg.act.p[0];
|
|
|
|
|
struct shared_context *shctx = shctx_ptr(cconf->c.cache);
|
2019-06-03 16:19:18 -04:00
|
|
|
struct shared_block *shblk;
|
|
|
|
|
unsigned int offset, sz;
|
|
|
|
|
unsigned int ret, total = 0;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
while (len) {
|
|
|
|
|
enum htx_blk_type type;
|
|
|
|
|
uint32_t info;
|
|
|
|
|
|
|
|
|
|
shblk = appctx->ctx.cache.next;
|
|
|
|
|
offset = appctx->ctx.cache.offset;
|
|
|
|
|
if (appctx->ctx.cache.rem_data) {
|
|
|
|
|
type = HTX_BLK_DATA;
|
|
|
|
|
info = 0;
|
|
|
|
|
goto add_data_blk;
|
|
|
|
|
}
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2020-04-02 06:25:26 -04:00
|
|
|
/* Get info of the next HTX block. May be split on 2 shblk */
|
2019-06-03 16:19:18 -04:00
|
|
|
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);
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
}
|
2019-06-03 16:19:18 -04:00
|
|
|
|
|
|
|
|
/* Get payload of the next HTX block and insert it. */
|
|
|
|
|
type = (info >> 28);
|
|
|
|
|
if (type != HTX_BLK_DATA)
|
|
|
|
|
ret = htx_cache_dump_blk(appctx, htx, type, info, shblk, offset);
|
|
|
|
|
else {
|
|
|
|
|
add_data_blk:
|
|
|
|
|
ret = htx_cache_dump_data_blk(appctx, htx, info, shblk, offset);
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
}
|
|
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
if (!ret)
|
|
|
|
|
break;
|
|
|
|
|
total += ret;
|
|
|
|
|
len -= ret;
|
|
|
|
|
|
|
|
|
|
if (appctx->ctx.cache.rem_data || type == mark)
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return total;
|
|
|
|
|
}
|
2019-06-03 16:19:18 -04:00
|
|
|
|
|
|
|
|
static int htx_cache_add_age_hdr(struct appctx *appctx, struct htx *htx)
|
|
|
|
|
{
|
|
|
|
|
struct cache_entry *cache_ptr = appctx->ctx.cache.entry;
|
|
|
|
|
unsigned int age;
|
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
|
|
chunk_reset(&trash);
|
|
|
|
|
age = MAX(0, (int)(now.tv_sec - cache_ptr->latest_validation)) + cache_ptr->age;
|
|
|
|
|
if (unlikely(age > CACHE_ENTRY_MAX_AGE))
|
|
|
|
|
age = CACHE_ENTRY_MAX_AGE;
|
|
|
|
|
end = ultoa_o(age, b_head(&trash), b_size(&trash));
|
|
|
|
|
b_set_data(&trash, end - b_head(&trash));
|
|
|
|
|
if (!http_add_header(htx, ist("Age"), ist2(b_head(&trash), b_data(&trash))))
|
|
|
|
|
return 0;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
static void http_cache_io_handler(struct appctx *appctx)
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
{
|
|
|
|
|
struct cache_entry *cache_ptr = appctx->ctx.cache.entry;
|
|
|
|
|
struct shared_block *first = block_ptr(cache_ptr);
|
|
|
|
|
struct stream_interface *si = appctx->owner;
|
|
|
|
|
struct channel *req = si_oc(si);
|
|
|
|
|
struct channel *res = si_ic(si);
|
|
|
|
|
struct htx *req_htx, *res_htx;
|
|
|
|
|
struct buffer *errmsg;
|
2019-06-03 16:19:18 -04:00
|
|
|
unsigned int len;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
size_t ret, total = 0;
|
|
|
|
|
|
|
|
|
|
res_htx = htxbuf(&res->buf);
|
2019-06-03 16:19:18 -04:00
|
|
|
total = res_htx->data;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
|
|
|
|
if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2020-04-02 06:25:26 -04:00
|
|
|
/* Check if the input buffer is available. */
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
if (!b_size(&res->buf)) {
|
|
|
|
|
si_rx_room_blk(si);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-15 18:37:45 -05:00
|
|
|
if (res->flags & (CF_SHUTW|CF_SHUTR|CF_SHUTW_NOW))
|
2018-12-15 18:35:15 -05:00
|
|
|
appctx->st0 = HTX_CACHE_END;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
|
|
|
|
if (appctx->st0 == HTX_CACHE_INIT) {
|
|
|
|
|
appctx->ctx.cache.next = block_ptr(cache_ptr);
|
|
|
|
|
appctx->ctx.cache.offset = sizeof(*cache_ptr);
|
|
|
|
|
appctx->ctx.cache.sent = 0;
|
2019-06-03 16:19:18 -04:00
|
|
|
appctx->ctx.cache.rem_data = 0;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
appctx->st0 = HTX_CACHE_HEADER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (appctx->st0 == HTX_CACHE_HEADER) {
|
|
|
|
|
/* Headers must be dump at once. Otherwise it is an error */
|
2019-06-03 16:19:18 -04:00
|
|
|
len = first->len - sizeof(*cache_ptr) - appctx->ctx.cache.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))
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
goto error;
|
|
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
/* Skip response body for HEAD requests */
|
|
|
|
|
if (si_strm(si)->txn->meth == HTTP_METH_HEAD)
|
2019-02-25 05:08:34 -05:00
|
|
|
appctx->st0 = HTX_CACHE_EOM;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
else
|
2019-06-03 16:19:18 -04:00
|
|
|
appctx->st0 = HTX_CACHE_DATA;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (appctx->st0 == HTX_CACHE_DATA) {
|
2019-06-03 16:19:18 -04:00
|
|
|
len = first->len - sizeof(*cache_ptr) - appctx->ctx.cache.sent;
|
|
|
|
|
if (len) {
|
|
|
|
|
ret = htx_cache_dump_msg(appctx, res_htx, len, HTX_BLK_EOM);
|
|
|
|
|
if (ret < len) {
|
|
|
|
|
si_rx_room_blk(si);
|
|
|
|
|
goto out;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
}
|
|
|
|
|
}
|
2019-06-03 16:19:18 -04:00
|
|
|
appctx->st0 = HTX_CACHE_END;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (appctx->st0 == HTX_CACHE_EOM) {
|
|
|
|
|
if (!htx_add_endof(res_htx, HTX_BLK_EOM)) {
|
|
|
|
|
si_rx_room_blk(si);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
appctx->st0 = HTX_CACHE_END;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end:
|
2019-02-25 05:40:49 -05:00
|
|
|
if (!(res->flags & CF_SHUTR) && appctx->st0 == HTX_CACHE_END) {
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
res->flags |= CF_READ_NULL;
|
|
|
|
|
si_shutr(si);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
2019-06-03 16:19:18 -04:00
|
|
|
total = res_htx->data - total;
|
2019-01-02 08:10:01 -05:00
|
|
|
if (total)
|
|
|
|
|
channel_add_input(res, total);
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
htx_to_buf(res_htx, &res->buf);
|
2019-02-25 05:40:49 -05:00
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
/* Sent and HTTP error 500 */
|
|
|
|
|
b_reset(&res->buf);
|
2019-07-17 16:02:08 -04:00
|
|
|
errmsg = &http_err_chunks[HTTP_ERR_500];
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
res->buf.data = b_data(errmsg);
|
|
|
|
|
memcpy(res->buf.area, b_head(errmsg), b_data(errmsg));
|
|
|
|
|
res_htx = htx_from_buf(&res->buf);
|
|
|
|
|
|
2019-06-03 16:19:18 -04:00
|
|
|
total = 0;
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
appctx->st0 = HTX_CACHE_END;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-12-07 11:34:39 -05:00
|
|
|
static int parse_cache_rule(struct proxy *proxy, const char *name, struct act_rule *rule, char **err)
|
2017-10-30 06:15:51 -04:00
|
|
|
{
|
|
|
|
|
struct flt_conf *fconf;
|
2018-12-07 11:34:39 -05:00
|
|
|
struct cache_flt_conf *cconf = NULL;
|
2017-10-30 06:15:51 -04:00
|
|
|
|
2018-12-07 11:34:39 -05:00
|
|
|
if (!*name || strcmp(name, "if") == 0 || strcmp(name, "unless") == 0) {
|
2017-10-30 06:15:51 -04:00
|
|
|
memprintf(err, "expects a cache name");
|
2018-12-07 11:34:39 -05:00
|
|
|
goto err;
|
2017-10-30 06:15:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check if a cache filter was already registered with this cache
|
|
|
|
|
* name, if that's the case, must use it. */
|
|
|
|
|
list_for_each_entry(fconf, &proxy->filter_configs, list) {
|
2018-12-07 11:34:39 -05:00
|
|
|
if (fconf->id == cache_store_flt_id) {
|
|
|
|
|
cconf = fconf->conf;
|
|
|
|
|
if (cconf && !strcmp((char *)cconf->c.name, name)) {
|
|
|
|
|
rule->arg.act.p[0] = cconf;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2017-10-30 06:15:51 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-07 11:34:39 -05:00
|
|
|
/* Create the filter cache config */
|
|
|
|
|
cconf = calloc(1, sizeof(*cconf));
|
|
|
|
|
if (!cconf) {
|
|
|
|
|
memprintf(err, "out of memory\n");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
2018-12-11 03:18:27 -05:00
|
|
|
cconf->flags = CACHE_FLT_F_IMPLICIT_DECL;
|
2018-12-07 11:34:39 -05:00
|
|
|
cconf->c.name = strdup(name);
|
|
|
|
|
if (!cconf->c.name) {
|
|
|
|
|
memprintf(err, "out of memory\n");
|
2017-10-30 06:15:51 -04:00
|
|
|
goto err;
|
|
|
|
|
}
|
2018-12-07 11:34:39 -05:00
|
|
|
|
2017-10-30 06:15:51 -04:00
|
|
|
/* register a filter to fill the cache buffer */
|
|
|
|
|
fconf = calloc(1, sizeof(*fconf));
|
|
|
|
|
if (!fconf) {
|
2018-12-07 11:34:39 -05:00
|
|
|
memprintf(err, "out of memory\n");
|
2017-10-30 06:15:51 -04:00
|
|
|
goto err;
|
|
|
|
|
}
|
2018-12-07 11:34:39 -05:00
|
|
|
fconf->id = cache_store_flt_id;
|
|
|
|
|
fconf->conf = cconf;
|
2017-10-30 06:15:51 -04:00
|
|
|
fconf->ops = &cache_ops;
|
|
|
|
|
LIST_ADDQ(&proxy->filter_configs, &fconf->list);
|
|
|
|
|
|
2018-12-07 11:34:39 -05:00
|
|
|
rule->arg.act.p[0] = cconf;
|
|
|
|
|
return 1;
|
2017-10-30 06:15:51 -04:00
|
|
|
|
2018-12-07 11:34:39 -05:00
|
|
|
err:
|
|
|
|
|
free(cconf);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2017-10-30 06:15:51 -04:00
|
|
|
|
2018-12-07 11:34:39 -05:00
|
|
|
enum act_parse_ret parse_cache_store(const char **args, int *orig_arg, struct proxy *proxy,
|
|
|
|
|
struct act_rule *rule, char **err)
|
|
|
|
|
{
|
|
|
|
|
rule->action = ACT_CUSTOM;
|
|
|
|
|
rule->action_ptr = http_action_store_cache;
|
|
|
|
|
|
|
|
|
|
if (!parse_cache_rule(proxy, args[*orig_arg], rule, err))
|
|
|
|
|
return ACT_RET_PRS_ERR;
|
|
|
|
|
|
|
|
|
|
(*orig_arg)++;
|
|
|
|
|
return ACT_RET_PRS_OK;
|
2017-10-30 06:15:51 -04:00
|
|
|
}
|
|
|
|
|
|
2019-08-05 10:55:32 -04:00
|
|
|
/* This produces a sha1 hash of the concatenation of the HTTP method,
|
|
|
|
|
* the first occurrence of the Host header followed by the path component
|
|
|
|
|
* if it begins with a slash ('/'). */
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
int sha1_hosturi(struct stream *s)
|
2017-11-23 13:43:17 -05:00
|
|
|
{
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
struct http_txn *txn = s->txn;
|
2019-07-15 15:01:29 -04:00
|
|
|
struct htx *htx = htxbuf(&s->req.buf);
|
|
|
|
|
struct htx_sl *sl;
|
|
|
|
|
struct http_hdr_ctx ctx;
|
2019-10-17 03:28:28 -04:00
|
|
|
struct ist uri;
|
2017-11-23 13:43:17 -05:00
|
|
|
blk_SHA_CTX sha1_ctx;
|
2018-07-13 05:56:34 -04:00
|
|
|
struct buffer *trash;
|
2017-11-23 13:43:17 -05:00
|
|
|
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
trash = get_trash_chunk();
|
2019-07-15 15:01:29 -04:00
|
|
|
ctx.blk = NULL;
|
2019-08-05 10:55:32 -04:00
|
|
|
|
|
|
|
|
switch (txn->meth) {
|
|
|
|
|
case HTTP_METH_HEAD:
|
|
|
|
|
case HTTP_METH_GET:
|
|
|
|
|
chunk_memcat(trash, "GET", 3);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-15 15:01:29 -04:00
|
|
|
sl = http_get_stline(htx);
|
2019-10-17 03:28:28 -04:00
|
|
|
uri = htx_sl_req_uri(sl); // whole uri
|
|
|
|
|
if (!uri.len)
|
2019-07-15 15:01:29 -04:00
|
|
|
return 0;
|
2019-10-17 03:28:28 -04:00
|
|
|
|
|
|
|
|
/* In HTTP/1, most URIs are seen in origin form ('/path/to/resource'),
|
|
|
|
|
* unless haproxy is deployed in front of an outbound cache. In HTTP/2,
|
|
|
|
|
* URIs are almost always sent in absolute form with their scheme. In
|
|
|
|
|
* this case, the scheme is almost always "https". In order to support
|
|
|
|
|
* sharing of cache objects between H1 and H2, we'll hash the absolute
|
|
|
|
|
* URI whenever known, or prepend "https://" + the Host header for
|
|
|
|
|
* relative URIs. The difference will only appear on absolute HTTP/1
|
|
|
|
|
* requests sent to an origin server, which practically is never met in
|
|
|
|
|
* the real world so we don't care about the ability to share the same
|
|
|
|
|
* key here.URIs are normalized from the absolute URI to an origin form as
|
|
|
|
|
* well.
|
|
|
|
|
*/
|
|
|
|
|
if (!(sl->flags & HTX_SL_F_HAS_AUTHORITY)) {
|
2019-10-29 08:02:15 -04:00
|
|
|
chunk_istcat(trash, ist("https://"));
|
2019-10-17 03:28:28 -04:00
|
|
|
if (!http_find_header(htx, ist("Host"), &ctx, 0))
|
|
|
|
|
return 0;
|
2019-10-29 08:02:15 -04:00
|
|
|
chunk_istcat(trash, ctx.value);
|
2019-10-17 03:28:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chunk_memcat(trash, uri.ptr, uri.len);
|
2017-11-23 13:43:17 -05:00
|
|
|
|
|
|
|
|
/* hash everything */
|
|
|
|
|
blk_SHA1_Init(&sha1_ctx);
|
2018-07-13 04:54:26 -04:00
|
|
|
blk_SHA1_Update(&sha1_ctx, trash->area, trash->data);
|
2017-11-23 13:43:17 -05:00
|
|
|
blk_SHA1_Final((unsigned char *)txn->cache_hash, &sha1_ctx);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-30 06:15:51 -04:00
|
|
|
enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *px,
|
|
|
|
|
struct session *sess, struct stream *s, int flags)
|
|
|
|
|
{
|
2017-10-31 15:43:01 -04:00
|
|
|
|
2019-02-25 04:59:33 -05:00
|
|
|
struct http_txn *txn = s->txn;
|
2017-10-31 15:43:01 -04:00
|
|
|
struct cache_entry *res;
|
2018-12-07 11:34:39 -05:00
|
|
|
struct cache_flt_conf *cconf = rule->arg.act.p[0];
|
|
|
|
|
struct cache *cache = cconf->c.cache;
|
2017-10-31 15:43:01 -04:00
|
|
|
|
2019-10-01 11:59:17 -04:00
|
|
|
/* Ignore cache for HTTP/1.0 requests and for requests other than GET
|
|
|
|
|
* and HEAD */
|
2019-02-25 04:59:33 -05:00
|
|
|
if (!(txn->req.flags & HTTP_MSGF_VER_11) ||
|
2019-10-01 11:59:17 -04:00
|
|
|
(txn->meth != HTTP_METH_GET && txn->meth != HTTP_METH_HEAD))
|
2019-02-25 04:59:33 -05:00
|
|
|
txn->flags |= TX_CACHE_IGNORE;
|
|
|
|
|
|
2019-07-16 08:54:53 -04:00
|
|
|
http_check_request_for_cacheability(s, &s->req);
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
|
2017-12-22 11:47:35 -05:00
|
|
|
if ((s->txn->flags & (TX_CACHE_IGNORE|TX_CACHEABLE)) == TX_CACHE_IGNORE)
|
|
|
|
|
return ACT_RET_CONT;
|
|
|
|
|
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
if (!sha1_hosturi(s))
|
2017-12-22 10:32:43 -05:00
|
|
|
return ACT_RET_CONT;
|
2017-11-23 13:43:17 -05:00
|
|
|
|
2017-12-22 11:47:35 -05:00
|
|
|
if (s->txn->flags & TX_CACHE_IGNORE)
|
|
|
|
|
return ACT_RET_CONT;
|
|
|
|
|
|
2018-12-14 08:00:25 -05:00
|
|
|
if (px == strm_fe(s))
|
2019-03-08 12:49:24 -05:00
|
|
|
_HA_ATOMIC_ADD(&px->fe_counters.p.http.cache_lookups, 1);
|
2018-12-14 08:00:25 -05:00
|
|
|
else
|
2019-03-08 12:49:24 -05:00
|
|
|
_HA_ATOMIC_ADD(&px->be_counters.p.http.cache_lookups, 1);
|
2018-12-14 08:00:25 -05:00
|
|
|
|
2017-11-20 13:13:12 -05:00
|
|
|
shctx_lock(shctx_ptr(cache));
|
2017-11-23 13:43:17 -05:00
|
|
|
res = entry_exist(cache, s->txn->cache_hash);
|
2017-10-31 15:43:01 -04:00
|
|
|
if (res) {
|
|
|
|
|
struct appctx *appctx;
|
2017-11-20 13:13:12 -05:00
|
|
|
shctx_row_inc_hot(shctx_ptr(cache), block_ptr(res));
|
|
|
|
|
shctx_unlock(shctx_ptr(cache));
|
2017-10-31 15:43:01 -04:00
|
|
|
s->target = &http_cache_applet.obj_type;
|
2018-12-19 09:19:27 -05:00
|
|
|
if ((appctx = si_register_handler(&s->si[1], objt_applet(s->target)))) {
|
2019-07-15 15:01:29 -04:00
|
|
|
appctx->st0 = HTX_CACHE_INIT;
|
2017-10-31 15:43:01 -04:00
|
|
|
appctx->rule = rule;
|
|
|
|
|
appctx->ctx.cache.entry = res;
|
2018-10-22 12:01:48 -04:00
|
|
|
appctx->ctx.cache.next = NULL;
|
|
|
|
|
appctx->ctx.cache.sent = 0;
|
2018-12-14 08:00:25 -05:00
|
|
|
|
|
|
|
|
if (px == strm_fe(s))
|
2019-03-08 12:49:24 -05:00
|
|
|
_HA_ATOMIC_ADD(&px->fe_counters.p.http.cache_hits, 1);
|
2018-12-14 08:00:25 -05:00
|
|
|
else
|
2019-03-08 12:49:24 -05:00
|
|
|
_HA_ATOMIC_ADD(&px->be_counters.p.http.cache_hits, 1);
|
2017-11-01 09:04:02 -04:00
|
|
|
return ACT_RET_CONT;
|
2017-10-31 15:43:01 -04:00
|
|
|
} else {
|
2017-11-21 14:01:28 -05:00
|
|
|
shctx_lock(shctx_ptr(cache));
|
|
|
|
|
shctx_row_dec_hot(shctx_ptr(cache), block_ptr(res));
|
|
|
|
|
shctx_unlock(shctx_ptr(cache));
|
2017-11-01 09:04:02 -04:00
|
|
|
return ACT_RET_YIELD;
|
2017-10-31 15:43:01 -04:00
|
|
|
}
|
|
|
|
|
}
|
2017-11-20 13:13:12 -05:00
|
|
|
shctx_unlock(shctx_ptr(cache));
|
2017-11-01 09:04:02 -04:00
|
|
|
return ACT_RET_CONT;
|
2017-10-30 06:15:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum act_parse_ret parse_cache_use(const char **args, int *orig_arg, struct proxy *proxy,
|
|
|
|
|
struct act_rule *rule, char **err)
|
|
|
|
|
{
|
|
|
|
|
rule->action = ACT_CUSTOM;
|
|
|
|
|
rule->action_ptr = http_action_req_cache_use;
|
|
|
|
|
|
2018-12-07 11:34:39 -05:00
|
|
|
if (!parse_cache_rule(proxy, args[*orig_arg], rule, err))
|
2017-10-30 06:15:51 -04:00
|
|
|
return ACT_RET_PRS_ERR;
|
|
|
|
|
|
|
|
|
|
(*orig_arg)++;
|
|
|
|
|
return ACT_RET_PRS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int cfg_parse_cache(const char *file, int linenum, char **args, int kwm)
|
|
|
|
|
{
|
|
|
|
|
int err_code = 0;
|
|
|
|
|
|
|
|
|
|
if (strcmp(args[0], "cache") == 0) { /* new cache section */
|
|
|
|
|
|
|
|
|
|
if (!*args[1]) {
|
2017-11-24 10:50:31 -05:00
|
|
|
ha_alert("parsing [%s:%d] : '%s' expects an <id> argument\n",
|
|
|
|
|
file, linenum, args[0]);
|
2017-10-30 06:15:51 -04:00
|
|
|
err_code |= ERR_ALERT | ERR_ABORT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
|
|
|
|
|
err_code |= ERR_ABORT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tmp_cache_config == NULL) {
|
|
|
|
|
tmp_cache_config = calloc(1, sizeof(*tmp_cache_config));
|
|
|
|
|
if (!tmp_cache_config) {
|
2017-11-24 10:50:31 -05:00
|
|
|
ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
|
2017-10-30 06:15:51 -04:00
|
|
|
err_code |= ERR_ALERT | ERR_ABORT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
strlcpy2(tmp_cache_config->id, args[1], 33);
|
|
|
|
|
if (strlen(args[1]) > 32) {
|
2017-11-24 10:50:31 -05:00
|
|
|
ha_warning("parsing [%s:%d]: cache id is limited to 32 characters, truncate to '%s'.\n",
|
|
|
|
|
file, linenum, tmp_cache_config->id);
|
2017-10-30 06:15:51 -04:00
|
|
|
err_code |= ERR_WARN;
|
|
|
|
|
}
|
2017-11-24 12:53:43 -05:00
|
|
|
tmp_cache_config->maxage = 60;
|
2017-10-30 06:15:51 -04:00
|
|
|
tmp_cache_config->maxblocks = 0;
|
2018-10-22 10:59:13 -04:00
|
|
|
tmp_cache_config->maxobjsz = 0;
|
2017-10-30 06:15:51 -04:00
|
|
|
}
|
|
|
|
|
} else if (strcmp(args[0], "total-max-size") == 0) {
|
2018-10-25 14:17:45 -04:00
|
|
|
unsigned long int maxsize;
|
|
|
|
|
char *err;
|
2017-10-30 06:15:51 -04:00
|
|
|
|
|
|
|
|
if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
|
|
|
|
|
err_code |= ERR_ABORT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-25 14:17:45 -04:00
|
|
|
maxsize = strtoul(args[1], &err, 10);
|
|
|
|
|
if (err == args[1] || *err != '\0') {
|
|
|
|
|
ha_warning("parsing [%s:%d]: total-max-size wrong value '%s'\n",
|
|
|
|
|
file, linenum, args[1]);
|
|
|
|
|
err_code |= ERR_ABORT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (maxsize > (UINT_MAX >> 20)) {
|
|
|
|
|
ha_warning("parsing [%s:%d]: \"total-max-size\" (%s) must not be greater than %u\n",
|
|
|
|
|
file, linenum, args[1], UINT_MAX >> 20);
|
|
|
|
|
err_code |= ERR_ABORT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-30 06:15:51 -04:00
|
|
|
/* size in megabytes */
|
2018-10-25 14:17:45 -04:00
|
|
|
maxsize *= 1024 * 1024 / CACHE_BLOCKSIZE;
|
2017-10-30 06:15:51 -04:00
|
|
|
tmp_cache_config->maxblocks = maxsize;
|
2017-11-24 12:53:43 -05:00
|
|
|
} else if (strcmp(args[0], "max-age") == 0) {
|
|
|
|
|
if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
|
|
|
|
|
err_code |= ERR_ABORT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*args[1]) {
|
|
|
|
|
ha_warning("parsing [%s:%d]: '%s' expects an age parameter in seconds.\n",
|
|
|
|
|
file, linenum, args[0]);
|
|
|
|
|
err_code |= ERR_WARN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp_cache_config->maxage = atoi(args[1]);
|
2018-10-22 10:59:13 -04:00
|
|
|
} else if (strcmp(args[0], "max-object-size") == 0) {
|
2018-10-25 14:29:31 -04:00
|
|
|
unsigned int maxobjsz;
|
|
|
|
|
char *err;
|
|
|
|
|
|
2018-10-22 10:59:13 -04:00
|
|
|
if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
|
|
|
|
|
err_code |= ERR_ABORT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*args[1]) {
|
|
|
|
|
ha_warning("parsing [%s:%d]: '%s' expects a maximum file size parameter in bytes.\n",
|
|
|
|
|
file, linenum, args[0]);
|
|
|
|
|
err_code |= ERR_WARN;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-25 14:29:31 -04:00
|
|
|
maxobjsz = strtoul(args[1], &err, 10);
|
|
|
|
|
if (err == args[1] || *err != '\0') {
|
|
|
|
|
ha_warning("parsing [%s:%d]: max-object-size wrong value '%s'\n",
|
|
|
|
|
file, linenum, args[1]);
|
|
|
|
|
err_code |= ERR_ABORT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
tmp_cache_config->maxobjsz = maxobjsz;
|
2018-10-22 10:59:13 -04:00
|
|
|
}
|
|
|
|
|
else if (*args[0] != 0) {
|
2017-11-24 10:50:31 -05:00
|
|
|
ha_alert("parsing [%s:%d] : unknown keyword '%s' in 'cache' section\n", file, linenum, args[0]);
|
2017-10-30 06:15:51 -04:00
|
|
|
err_code |= ERR_ALERT | ERR_FATAL;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
out:
|
|
|
|
|
return err_code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* once the cache section is parsed */
|
|
|
|
|
|
|
|
|
|
int cfg_post_parse_section_cache()
|
|
|
|
|
{
|
|
|
|
|
int err_code = 0;
|
|
|
|
|
|
|
|
|
|
if (tmp_cache_config) {
|
|
|
|
|
|
|
|
|
|
if (tmp_cache_config->maxblocks <= 0) {
|
2017-11-24 10:50:31 -05:00
|
|
|
ha_alert("Size not specified for cache '%s'\n", tmp_cache_config->id);
|
2017-10-30 06:15:51 -04:00
|
|
|
err_code |= ERR_FATAL | ERR_ALERT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-25 14:29:31 -04:00
|
|
|
if (!tmp_cache_config->maxobjsz) {
|
2018-10-22 10:59:13 -04:00
|
|
|
/* Default max. file size is a 256th of the cache size. */
|
|
|
|
|
tmp_cache_config->maxobjsz =
|
|
|
|
|
(tmp_cache_config->maxblocks * CACHE_BLOCKSIZE) >> 8;
|
2018-10-25 14:29:31 -04:00
|
|
|
}
|
|
|
|
|
else if (tmp_cache_config->maxobjsz > tmp_cache_config->maxblocks * CACHE_BLOCKSIZE / 2) {
|
|
|
|
|
ha_alert("\"max-object-size\" is limited to an half of \"total-max-size\" => %u\n", tmp_cache_config->maxblocks * CACHE_BLOCKSIZE / 2);
|
|
|
|
|
err_code |= ERR_FATAL | ERR_ALERT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2018-10-22 10:59:13 -04:00
|
|
|
|
2019-08-28 09:22:49 -04:00
|
|
|
/* add to the list of cache to init and reinit tmp_cache_config
|
|
|
|
|
* for next cache section, if any.
|
|
|
|
|
*/
|
|
|
|
|
LIST_ADDQ(&caches_config, &tmp_cache_config->list);
|
|
|
|
|
tmp_cache_config = NULL;
|
|
|
|
|
return err_code;
|
|
|
|
|
}
|
|
|
|
|
out:
|
|
|
|
|
free(tmp_cache_config);
|
|
|
|
|
tmp_cache_config = NULL;
|
|
|
|
|
return err_code;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int post_check_cache()
|
|
|
|
|
{
|
|
|
|
|
struct proxy *px;
|
|
|
|
|
struct cache *back, *cache_config, *cache;
|
|
|
|
|
struct shared_context *shctx;
|
|
|
|
|
int ret_shctx;
|
|
|
|
|
int err_code = 0;
|
|
|
|
|
|
|
|
|
|
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), 1);
|
2017-10-31 09:33:34 -04:00
|
|
|
|
2018-10-25 14:18:59 -04:00
|
|
|
if (ret_shctx <= 0) {
|
2017-10-30 06:15:51 -04:00
|
|
|
if (ret_shctx == SHCTX_E_INIT_LOCK)
|
2017-11-24 10:50:31 -05:00
|
|
|
ha_alert("Unable to initialize the lock for the cache.\n");
|
2017-10-30 06:15:51 -04:00
|
|
|
else
|
2017-11-24 10:50:31 -05:00
|
|
|
ha_alert("Unable to allocate cache.\n");
|
2017-10-30 06:15:51 -04:00
|
|
|
|
|
|
|
|
err_code |= ERR_FATAL | ERR_ALERT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2017-11-20 13:13:12 -05:00
|
|
|
shctx->free_block = cache_free_blocks;
|
2019-08-28 09:22:49 -04:00
|
|
|
/* the cache structure is stored in the shctx and added to the
|
|
|
|
|
* caches list, we can remove the entry from the caches_config
|
|
|
|
|
* list */
|
|
|
|
|
memcpy(shctx->data, cache_config, sizeof(struct cache));
|
2017-10-30 06:15:51 -04:00
|
|
|
cache = (struct cache *)shctx->data;
|
|
|
|
|
cache->entries = EB_ROOT_UNIQUE;
|
|
|
|
|
LIST_ADDQ(&caches, &cache->list);
|
2019-08-28 09:22:49 -04:00
|
|
|
LIST_DEL(&cache_config->list);
|
|
|
|
|
free(cache_config);
|
|
|
|
|
|
|
|
|
|
/* Find all references for this cache in the existing filters
|
|
|
|
|
* (over all proxies) and reference it in matching filters.
|
|
|
|
|
*/
|
|
|
|
|
for (px = proxies_list; px; px = px->next) {
|
|
|
|
|
struct flt_conf *fconf;
|
|
|
|
|
struct cache_flt_conf *cconf;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(fconf, &px->filter_configs, list) {
|
|
|
|
|
if (fconf->id != cache_store_flt_id)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
cconf = fconf->conf;
|
|
|
|
|
if (!strcmp(cache->id, cconf->c.name)) {
|
|
|
|
|
free(cconf->c.name);
|
|
|
|
|
cconf->c.cache = cache;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-30 06:15:51 -04:00
|
|
|
}
|
2019-08-28 09:22:49 -04:00
|
|
|
|
2017-10-30 06:15:51 -04:00
|
|
|
out:
|
|
|
|
|
return err_code;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct flt_ops cache_ops = {
|
|
|
|
|
.init = cache_store_init,
|
2018-12-07 11:34:39 -05:00
|
|
|
.check = cache_store_check,
|
|
|
|
|
.deinit = cache_store_deinit,
|
2017-10-30 06:15:51 -04:00
|
|
|
|
2020-03-06 08:52:06 -05:00
|
|
|
/* Handle stream init/deinit */
|
|
|
|
|
.attach = cache_store_strm_init,
|
|
|
|
|
.detach = cache_store_strm_deinit,
|
|
|
|
|
|
2017-10-31 09:33:34 -04:00
|
|
|
/* Handle channels activity */
|
2019-01-07 10:12:07 -05:00
|
|
|
.channel_post_analyze = cache_store_post_analyze,
|
2017-10-31 09:33:34 -04:00
|
|
|
|
|
|
|
|
/* Filter HTTP requests and responses */
|
|
|
|
|
.http_headers = cache_store_http_headers,
|
MEDIUM: cache/htx: Add the HTX support into the cache
The cache is now able to store and resend HTX messages. When an HTX message is
stored in the cache, the headers are prefixed with their block's info (an
uint32_t), containing its type and its length. Data, on their side, are stored
without any prefix. Only the value is copied in the cache. 2 fields have been
added in the structure cache_entry, hdrs_len and data_len, to known the size, in
the cache, of the headers part and the data part. If the message is chunked, the
trailers are also copied, the same way as data. When the HTX message is
recreated in the cache applet, the trailers size is known removing the headers
length and the data lenght from the total object length.
2018-12-07 06:21:11 -05:00
|
|
|
.http_payload = cache_store_http_payload,
|
2017-10-31 09:33:34 -04:00
|
|
|
.http_end = cache_store_http_end,
|
2017-10-30 06:15:51 -04:00
|
|
|
};
|
|
|
|
|
|
2018-12-11 03:18:27 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
parse_cache_flt(char **args, int *cur_arg, struct proxy *px,
|
|
|
|
|
struct flt_conf *fconf, char **err, void *private)
|
|
|
|
|
{
|
|
|
|
|
struct flt_conf *f, *back;
|
2018-12-14 04:19:28 -05:00
|
|
|
struct cache_flt_conf *cconf = NULL;
|
2018-12-11 03:18:27 -05:00
|
|
|
char *name = NULL;
|
|
|
|
|
int pos = *cur_arg;
|
|
|
|
|
|
2020-05-18 05:58:16 -04:00
|
|
|
/* Get the cache filter name. <pos> point on "cache" keyword */
|
|
|
|
|
if (!*args[pos + 1]) {
|
|
|
|
|
memprintf(err, "%s : expects an <id> argument", args[pos]);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
name = strdup(args[pos + 1]);
|
|
|
|
|
if (!name) {
|
|
|
|
|
memprintf(err, "%s '%s' : out of memory", args[pos], args[pos + 1]);
|
|
|
|
|
goto error;
|
2018-12-11 03:18:27 -05:00
|
|
|
}
|
2020-05-18 05:58:16 -04:00
|
|
|
pos += 2;
|
2018-12-11 03:18:27 -05:00
|
|
|
|
|
|
|
|
/* Check if an implicit filter with the same name already exists. If so,
|
|
|
|
|
* we remove the implicit filter to use the explicit one. */
|
|
|
|
|
list_for_each_entry_safe(f, back, &px->filter_configs, list) {
|
|
|
|
|
if (f->id != cache_store_flt_id)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
cconf = f->conf;
|
|
|
|
|
if (strcmp(name, cconf->c.name)) {
|
|
|
|
|
cconf = NULL;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(cconf->flags & CACHE_FLT_F_IMPLICIT_DECL)) {
|
|
|
|
|
cconf = NULL;
|
|
|
|
|
memprintf(err, "%s: multiple explicit declarations of the cache filter '%s'",
|
|
|
|
|
px->id, name);
|
2020-01-17 19:46:18 -05:00
|
|
|
goto error;
|
2018-12-11 03:18:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remove the implicit filter. <cconf> is kept for the explicit one */
|
|
|
|
|
LIST_DEL(&f->list);
|
|
|
|
|
free(f);
|
|
|
|
|
free(name);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No implicit cache filter found, create configuration for the explicit one */
|
|
|
|
|
if (!cconf) {
|
|
|
|
|
cconf = calloc(1, sizeof(*cconf));
|
|
|
|
|
if (!cconf) {
|
|
|
|
|
memprintf(err, "%s: out of memory", args[*cur_arg]);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
cconf->c.name = name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cconf->flags = 0;
|
|
|
|
|
fconf->id = cache_store_flt_id;
|
|
|
|
|
fconf->conf = cconf;
|
|
|
|
|
fconf->ops = &cache_ops;
|
|
|
|
|
|
|
|
|
|
*cur_arg = pos;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
free(name);
|
|
|
|
|
free(cconf);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-18 07:26:46 -04:00
|
|
|
static int cli_parse_show_cache(char **args, char *payload, struct appctx *appctx, void *private)
|
MEDIUM: cache: "show cache" on the cli
The cli command "show cache" displays the status of the cache, the first
displayed line is the shctx informations with how much blocks available
blocks it contains (blocks are 1k by default).
The next lines are the objects stored in the cache tree, the pointer,
the size of the object and how much blocks it uses, a refcount for the
number of users of the object, and the remaining expiration time (which
can be negative if expired)
Example:
$ echo "show cache" | socat - /run/haproxy.sock
0x7fa54e9ab03a: foobar (shctx:0x7fa54e9ab000, available blocks:3921)
0x7fa54ed65b8c (size: 43190 (43 blocks), refcount:2, expire: 2)
0x7fa54ecf1b4c (size: 45238 (45 blocks), refcount:0, expire: 2)
0x7fa54ed70cec (size: 61622 (61 blocks), refcount:0, expire: 2)
0x7fa54ecdbcac (size: 42166 (42 blocks), refcount:1, expire: 2)
0x7fa54ec9736c (size: 44214 (44 blocks), refcount:2, expire: 2)
0x7fa54eca28ec (size: 46262 (46 blocks), refcount:2, expire: -2)
2017-11-21 14:01:26 -05:00
|
|
|
{
|
|
|
|
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int cli_io_handler_show_cache(struct appctx *appctx)
|
|
|
|
|
{
|
|
|
|
|
struct cache* cache = appctx->ctx.cli.p0;
|
|
|
|
|
struct stream_interface *si = appctx->owner;
|
|
|
|
|
|
|
|
|
|
if (cache == NULL) {
|
|
|
|
|
cache = LIST_ELEM((caches).n, typeof(struct cache *), list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list_for_each_entry_from(cache, &caches, list) {
|
|
|
|
|
struct eb32_node *node = NULL;
|
|
|
|
|
unsigned int next_key;
|
|
|
|
|
struct cache_entry *entry;
|
|
|
|
|
|
|
|
|
|
next_key = appctx->ctx.cli.i0;
|
2018-04-04 05:56:43 -04:00
|
|
|
if (!next_key) {
|
|
|
|
|
chunk_printf(&trash, "%p: %s (shctx:%p, available blocks:%d)\n", cache, cache->id, shctx_ptr(cache), shctx_ptr(cache)->nbav);
|
|
|
|
|
if (ci_putchk(si_ic(si), &trash) == -1) {
|
2018-11-15 05:08:52 -05:00
|
|
|
si_rx_room_blk(si);
|
2018-04-04 05:56:43 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
MEDIUM: cache: "show cache" on the cli
The cli command "show cache" displays the status of the cache, the first
displayed line is the shctx informations with how much blocks available
blocks it contains (blocks are 1k by default).
The next lines are the objects stored in the cache tree, the pointer,
the size of the object and how much blocks it uses, a refcount for the
number of users of the object, and the remaining expiration time (which
can be negative if expired)
Example:
$ echo "show cache" | socat - /run/haproxy.sock
0x7fa54e9ab03a: foobar (shctx:0x7fa54e9ab000, available blocks:3921)
0x7fa54ed65b8c (size: 43190 (43 blocks), refcount:2, expire: 2)
0x7fa54ecf1b4c (size: 45238 (45 blocks), refcount:0, expire: 2)
0x7fa54ed70cec (size: 61622 (61 blocks), refcount:0, expire: 2)
0x7fa54ecdbcac (size: 42166 (42 blocks), refcount:1, expire: 2)
0x7fa54ec9736c (size: 44214 (44 blocks), refcount:2, expire: 2)
0x7fa54eca28ec (size: 46262 (46 blocks), refcount:2, expire: -2)
2017-11-21 14:01:26 -05:00
|
|
|
|
|
|
|
|
appctx->ctx.cli.p0 = cache;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
|
|
shctx_lock(shctx_ptr(cache));
|
|
|
|
|
node = eb32_lookup_ge(&cache->entries, next_key);
|
|
|
|
|
if (!node) {
|
|
|
|
|
shctx_unlock(shctx_ptr(cache));
|
2018-04-04 05:56:43 -04:00
|
|
|
appctx->ctx.cli.i0 = 0;
|
MEDIUM: cache: "show cache" on the cli
The cli command "show cache" displays the status of the cache, the first
displayed line is the shctx informations with how much blocks available
blocks it contains (blocks are 1k by default).
The next lines are the objects stored in the cache tree, the pointer,
the size of the object and how much blocks it uses, a refcount for the
number of users of the object, and the remaining expiration time (which
can be negative if expired)
Example:
$ echo "show cache" | socat - /run/haproxy.sock
0x7fa54e9ab03a: foobar (shctx:0x7fa54e9ab000, available blocks:3921)
0x7fa54ed65b8c (size: 43190 (43 blocks), refcount:2, expire: 2)
0x7fa54ecf1b4c (size: 45238 (45 blocks), refcount:0, expire: 2)
0x7fa54ed70cec (size: 61622 (61 blocks), refcount:0, expire: 2)
0x7fa54ecdbcac (size: 42166 (42 blocks), refcount:1, expire: 2)
0x7fa54ec9736c (size: 44214 (44 blocks), refcount:2, expire: 2)
0x7fa54eca28ec (size: 46262 (46 blocks), refcount:2, expire: -2)
2017-11-21 14:01:26 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entry = container_of(node, struct cache_entry, eb);
|
2020-02-25 03:35:07 -05:00
|
|
|
chunk_printf(&trash, "%p hash:%u size:%u (%u blocks), refcount:%u, expire:%d\n", entry, read_u32(entry->hash), block_ptr(entry)->len, block_ptr(entry)->block_count, block_ptr(entry)->refcount, entry->expire - (int)now.tv_sec);
|
MEDIUM: cache: "show cache" on the cli
The cli command "show cache" displays the status of the cache, the first
displayed line is the shctx informations with how much blocks available
blocks it contains (blocks are 1k by default).
The next lines are the objects stored in the cache tree, the pointer,
the size of the object and how much blocks it uses, a refcount for the
number of users of the object, and the remaining expiration time (which
can be negative if expired)
Example:
$ echo "show cache" | socat - /run/haproxy.sock
0x7fa54e9ab03a: foobar (shctx:0x7fa54e9ab000, available blocks:3921)
0x7fa54ed65b8c (size: 43190 (43 blocks), refcount:2, expire: 2)
0x7fa54ecf1b4c (size: 45238 (45 blocks), refcount:0, expire: 2)
0x7fa54ed70cec (size: 61622 (61 blocks), refcount:0, expire: 2)
0x7fa54ecdbcac (size: 42166 (42 blocks), refcount:1, expire: 2)
0x7fa54ec9736c (size: 44214 (44 blocks), refcount:2, expire: 2)
0x7fa54eca28ec (size: 46262 (46 blocks), refcount:2, expire: -2)
2017-11-21 14:01:26 -05:00
|
|
|
|
|
|
|
|
next_key = node->key + 1;
|
|
|
|
|
appctx->ctx.cli.i0 = next_key;
|
|
|
|
|
|
|
|
|
|
shctx_unlock(shctx_ptr(cache));
|
|
|
|
|
|
|
|
|
|
if (ci_putchk(si_ic(si), &trash) == -1) {
|
2018-11-15 05:08:52 -05:00
|
|
|
si_rx_room_blk(si);
|
MEDIUM: cache: "show cache" on the cli
The cli command "show cache" displays the status of the cache, the first
displayed line is the shctx informations with how much blocks available
blocks it contains (blocks are 1k by default).
The next lines are the objects stored in the cache tree, the pointer,
the size of the object and how much blocks it uses, a refcount for the
number of users of the object, and the remaining expiration time (which
can be negative if expired)
Example:
$ echo "show cache" | socat - /run/haproxy.sock
0x7fa54e9ab03a: foobar (shctx:0x7fa54e9ab000, available blocks:3921)
0x7fa54ed65b8c (size: 43190 (43 blocks), refcount:2, expire: 2)
0x7fa54ecf1b4c (size: 45238 (45 blocks), refcount:0, expire: 2)
0x7fa54ed70cec (size: 61622 (61 blocks), refcount:0, expire: 2)
0x7fa54ecdbcac (size: 42166 (42 blocks), refcount:1, expire: 2)
0x7fa54ec9736c (size: 44214 (44 blocks), refcount:2, expire: 2)
0x7fa54eca28ec (size: 46262 (46 blocks), refcount:2, expire: -2)
2017-11-21 14:01:26 -05:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-11 03:18:27 -05:00
|
|
|
/* Declare the filter parser for "cache" keyword */
|
|
|
|
|
static struct flt_kw_list filter_kws = { "CACHE", { }, {
|
|
|
|
|
{ "cache", parse_cache_flt, NULL },
|
|
|
|
|
{ NULL, NULL, NULL },
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
INITCALL1(STG_REGISTER, flt_register_keywords, &filter_kws);
|
|
|
|
|
|
MEDIUM: cache: "show cache" on the cli
The cli command "show cache" displays the status of the cache, the first
displayed line is the shctx informations with how much blocks available
blocks it contains (blocks are 1k by default).
The next lines are the objects stored in the cache tree, the pointer,
the size of the object and how much blocks it uses, a refcount for the
number of users of the object, and the remaining expiration time (which
can be negative if expired)
Example:
$ echo "show cache" | socat - /run/haproxy.sock
0x7fa54e9ab03a: foobar (shctx:0x7fa54e9ab000, available blocks:3921)
0x7fa54ed65b8c (size: 43190 (43 blocks), refcount:2, expire: 2)
0x7fa54ecf1b4c (size: 45238 (45 blocks), refcount:0, expire: 2)
0x7fa54ed70cec (size: 61622 (61 blocks), refcount:0, expire: 2)
0x7fa54ecdbcac (size: 42166 (42 blocks), refcount:1, expire: 2)
0x7fa54ec9736c (size: 44214 (44 blocks), refcount:2, expire: 2)
0x7fa54eca28ec (size: 46262 (46 blocks), refcount:2, expire: -2)
2017-11-21 14:01:26 -05:00
|
|
|
static struct cli_kw_list cli_kws = {{},{
|
2017-11-22 10:41:26 -05:00
|
|
|
{ { "show", "cache", NULL }, "show cache : show cache status", cli_parse_show_cache, cli_io_handler_show_cache, NULL, NULL },
|
|
|
|
|
{{},}
|
MEDIUM: cache: "show cache" on the cli
The cli command "show cache" displays the status of the cache, the first
displayed line is the shctx informations with how much blocks available
blocks it contains (blocks are 1k by default).
The next lines are the objects stored in the cache tree, the pointer,
the size of the object and how much blocks it uses, a refcount for the
number of users of the object, and the remaining expiration time (which
can be negative if expired)
Example:
$ echo "show cache" | socat - /run/haproxy.sock
0x7fa54e9ab03a: foobar (shctx:0x7fa54e9ab000, available blocks:3921)
0x7fa54ed65b8c (size: 43190 (43 blocks), refcount:2, expire: 2)
0x7fa54ecf1b4c (size: 45238 (45 blocks), refcount:0, expire: 2)
0x7fa54ed70cec (size: 61622 (61 blocks), refcount:0, expire: 2)
0x7fa54ecdbcac (size: 42166 (42 blocks), refcount:1, expire: 2)
0x7fa54ec9736c (size: 44214 (44 blocks), refcount:2, expire: 2)
0x7fa54eca28ec (size: 46262 (46 blocks), refcount:2, expire: -2)
2017-11-21 14:01:26 -05:00
|
|
|
}};
|
|
|
|
|
|
2018-11-25 13:14:37 -05:00
|
|
|
INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
|
MEDIUM: cache: "show cache" on the cli
The cli command "show cache" displays the status of the cache, the first
displayed line is the shctx informations with how much blocks available
blocks it contains (blocks are 1k by default).
The next lines are the objects stored in the cache tree, the pointer,
the size of the object and how much blocks it uses, a refcount for the
number of users of the object, and the remaining expiration time (which
can be negative if expired)
Example:
$ echo "show cache" | socat - /run/haproxy.sock
0x7fa54e9ab03a: foobar (shctx:0x7fa54e9ab000, available blocks:3921)
0x7fa54ed65b8c (size: 43190 (43 blocks), refcount:2, expire: 2)
0x7fa54ecf1b4c (size: 45238 (45 blocks), refcount:0, expire: 2)
0x7fa54ed70cec (size: 61622 (61 blocks), refcount:0, expire: 2)
0x7fa54ecdbcac (size: 42166 (42 blocks), refcount:1, expire: 2)
0x7fa54ec9736c (size: 44214 (44 blocks), refcount:2, expire: 2)
0x7fa54eca28ec (size: 46262 (46 blocks), refcount:2, expire: -2)
2017-11-21 14:01:26 -05:00
|
|
|
|
2017-10-30 06:15:51 -04:00
|
|
|
static struct action_kw_list http_res_actions = {
|
|
|
|
|
.kw = {
|
|
|
|
|
{ "cache-store", parse_cache_store },
|
|
|
|
|
{ NULL, NULL }
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-11-25 13:14:37 -05:00
|
|
|
INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
|
|
|
|
|
|
2017-10-30 06:15:51 -04:00
|
|
|
static struct action_kw_list http_req_actions = {
|
|
|
|
|
.kw = {
|
|
|
|
|
{ "cache-use", parse_cache_use },
|
|
|
|
|
{ NULL, NULL }
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-11-25 13:14:37 -05:00
|
|
|
INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
|
|
|
|
|
|
2019-03-29 13:26:52 -04:00
|
|
|
struct applet http_cache_applet = {
|
2017-10-30 06:15:51 -04:00
|
|
|
.obj_type = OBJ_TYPE_APPLET,
|
|
|
|
|
.name = "<CACHE>", /* used for logging */
|
2017-10-31 15:43:01 -04:00
|
|
|
.fct = http_cache_io_handler,
|
2017-11-24 08:33:55 -05:00
|
|
|
.release = http_cache_applet_release,
|
2017-10-30 06:15:51 -04:00
|
|
|
};
|
|
|
|
|
|
2018-11-26 05:33:13 -05:00
|
|
|
/* config parsers for this section */
|
|
|
|
|
REGISTER_CONFIG_SECTION("cache", cfg_parse_cache, cfg_post_parse_section_cache);
|
2019-08-28 09:22:49 -04:00
|
|
|
REGISTER_POST_CHECK(post_check_cache);
|