mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-21 22:01:31 +02:00
MEDIUM: cache: Add support for 'If-None-Match' request header
Partial support of conditional HTTP requests. This commit adds the support of the 'If-None-Match' header (see RFC 7232#3.2). When a client specifies a list of ETags through one or more 'If-None-Match' headers, they are all compared to the one that might have been stored in the corresponding http cache entry until one of them matches. If a match happens, a specific "304 Not Modified" response is sent instead of the cached data. This response has all the stored headers but no other data (see RFC 7232#4.1). Otherwise, the whole cached data is sent. Although unlikely in a GET/HEAD request, the "If-None-Match: *" syntax is valid and also receives a "304 Not Modified" response (RFC 7434#4.3.2). This resolves a part of GitHub issue #821.
This commit is contained in:
parent
dbb65b5a7a
commit
6cb10384a3
@ -112,6 +112,9 @@ struct appctx {
|
|||||||
unsigned int sent; /* The number of bytes already sent for this cache entry. */
|
unsigned int sent; /* The number of bytes already sent for this cache entry. */
|
||||||
unsigned int offset; /* start offset of remaining data relative to beginning of the next block */
|
unsigned int offset; /* start offset of remaining data relative to beginning of the next block */
|
||||||
unsigned int rem_data; /* Remaining bytes for the last data block (HTX only, 0 means process next block) */
|
unsigned int rem_data; /* Remaining bytes for the last data block (HTX only, 0 means process next block) */
|
||||||
|
unsigned int send_notmodified:1; /* In case of conditional request, we might want to send a "304 Not Modified"
|
||||||
|
* response instead of the stored data. */
|
||||||
|
unsigned int unused:31;
|
||||||
struct shared_block *next; /* The next block of data to be sent for this cache entry. */
|
struct shared_block *next; /* The next block of data to be sent for this cache entry. */
|
||||||
} cache;
|
} cache;
|
||||||
/* all entries below are used by various CLI commands, please
|
/* all entries below are used by various CLI commands, please
|
||||||
|
67
src/cache.c
67
src/cache.c
@ -933,8 +933,14 @@ static void http_cache_io_handler(struct appctx *appctx)
|
|||||||
!htx_cache_add_age_hdr(appctx, res_htx))
|
!htx_cache_add_age_hdr(appctx, res_htx))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
/* Skip response body for HEAD requests */
|
/* In case of a conditional request, we might want to send a
|
||||||
if (si_strm(si)->txn->meth == HTTP_METH_HEAD)
|
* "304 Not Modified" response instead of the stored data. */
|
||||||
|
if (appctx->ctx.cache.send_notmodified)
|
||||||
|
http_replace_res_status(res_htx, ist("304"), ist("Not Modified"));
|
||||||
|
|
||||||
|
/* Skip response body for HEAD requests or in case of "304 Not
|
||||||
|
* Modified" response. */
|
||||||
|
if (si_strm(si)->txn->meth == HTTP_METH_HEAD || appctx->ctx.cache.send_notmodified)
|
||||||
appctx->st0 = HTX_CACHE_EOM;
|
appctx->st0 = HTX_CACHE_EOM;
|
||||||
else
|
else
|
||||||
appctx->st0 = HTX_CACHE_DATA;
|
appctx->st0 = HTX_CACHE_DATA;
|
||||||
@ -1121,6 +1127,61 @@ int sha1_hosturi(struct stream *s)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Looks for "If-None-Match" headers in the request and compares their value
|
||||||
|
* with the one that might have been stored in the cache_entry. If any of them
|
||||||
|
* matches, a "304 Not Modified" response should be sent instead of the cached
|
||||||
|
* data.
|
||||||
|
* Although unlikely in a GET/HEAD request, the "If-None-Match: *" syntax is
|
||||||
|
* valid and should receive a "304 Not Modified" response (RFC 7434#4.3.2).
|
||||||
|
* Returns 1 if "304 Not Modified" should be sent, 0 otherwise.
|
||||||
|
*/
|
||||||
|
static int should_send_notmodified_response(struct cache *cache, struct htx *htx,
|
||||||
|
struct cache_entry *entry)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
struct http_hdr_ctx ctx = { .blk = NULL };
|
||||||
|
struct ist cache_entry_etag = IST_NULL;
|
||||||
|
struct buffer *etag_buffer = NULL;
|
||||||
|
|
||||||
|
if (entry->etag_length == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* If we find a "If-None-Match" header in the request, rebuild the
|
||||||
|
* cache_entry's ETag in order to perform comparisons. */
|
||||||
|
/* There could be multiple "if-none-match" header lines. */
|
||||||
|
while (http_find_header(htx, ist("if-none-match"), &ctx, 0)) {
|
||||||
|
|
||||||
|
/* A '*' matches everything. */
|
||||||
|
if (isteq(ctx.value, ist("*")) != 0) {
|
||||||
|
retval = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rebuild the stored ETag. */
|
||||||
|
if (etag_buffer == NULL) {
|
||||||
|
etag_buffer = get_trash_chunk();
|
||||||
|
|
||||||
|
if (shctx_row_data_get(shctx_ptr(cache), block_ptr(entry),
|
||||||
|
(unsigned char*)b_orig(etag_buffer),
|
||||||
|
entry->etag_offset, entry->etag_length) == 0) {
|
||||||
|
cache_entry_etag = ist2(b_orig(etag_buffer), entry->etag_length);
|
||||||
|
} else {
|
||||||
|
/* We could not rebuild the ETag in one go, we
|
||||||
|
* won't send a "304 Not Modified" response. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (http_compare_etags(cache_entry_etag, ctx.value) == 1) {
|
||||||
|
retval = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *px,
|
enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *px,
|
||||||
struct session *sess, struct stream *s, int flags)
|
struct session *sess, struct stream *s, int flags)
|
||||||
{
|
{
|
||||||
@ -1165,6 +1226,8 @@ enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *p
|
|||||||
appctx->ctx.cache.entry = res;
|
appctx->ctx.cache.entry = res;
|
||||||
appctx->ctx.cache.next = NULL;
|
appctx->ctx.cache.next = NULL;
|
||||||
appctx->ctx.cache.sent = 0;
|
appctx->ctx.cache.sent = 0;
|
||||||
|
appctx->ctx.cache.send_notmodified =
|
||||||
|
should_send_notmodified_response(cache, htxbuf(&s->req.buf), res);
|
||||||
|
|
||||||
if (px == strm_fe(s))
|
if (px == strm_fe(s))
|
||||||
_HA_ATOMIC_ADD(&px->fe_counters.p.http.cache_hits, 1);
|
_HA_ATOMIC_ADD(&px->fe_counters.p.http.cache_hits, 1);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user