diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index 0ae410b1c..50efff4c5 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -72,6 +72,7 @@ int apply_filters_to_request(struct stream *s, struct channel *req, struct proxy int apply_filters_to_response(struct stream *s, struct channel *rtr, struct proxy *px); void manage_client_side_cookies(struct stream *s, struct channel *req); void manage_server_side_cookies(struct stream *s, struct channel *rtr); +void check_request_for_cacheability(struct stream *s, struct channel *chn); void check_response_for_cacheability(struct stream *s, struct channel *rtr); int stats_check_uri(struct stream_interface *si, struct http_txn *txn, struct proxy *backend); void init_proto_http(); diff --git a/include/types/proto_http.h b/include/types/proto_http.h index 35a8cd218..b7b7c6cbc 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -68,9 +68,10 @@ /* cacheability management, bits values 0x1000 to 0x3000 (0-3 shift 12) */ #define TX_CACHEABLE 0x00001000 /* at least part of the response is cacheable */ #define TX_CACHE_COOK 0x00002000 /* a cookie in the response is cacheable */ +#define TX_CACHE_IGNORE 0x00004000 /* do not retrieve object from cache */ #define TX_CACHE_SHIFT 12 /* bit shift */ -/* Unused: 0x4000, 0x8000 */ +/* Unused: 0x8000 */ #define TX_WAIT_CLEANUP 0x0010000 /* this transaction is waiting for a clean up */ diff --git a/src/proto_http.c b/src/proto_http.c index 701ced43c..f585deed2 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -7679,6 +7679,95 @@ void manage_server_side_cookies(struct stream *s, struct channel *res) } +/* + * Parses the Cache-Control and Pragma request header fields to determine if + * the request may be served from the cache and/or if it is cacheable. Updates + * s->txn->flags. + */ +void check_request_for_cacheability(struct stream *s, struct channel *chn) +{ + struct http_txn *txn = s->txn; + char *p1, *p2; + char *cur_ptr, *cur_end, *cur_next; + int pragma_found; + int cc_found; + int cur_idx; + + if ((txn->flags & (TX_CACHEABLE|TX_CACHE_IGNORE)) == TX_CACHE_IGNORE) + return; /* nothing more to do here */ + + cur_idx = 0; + pragma_found = cc_found = 0; + cur_next = chn->buf->p + hdr_idx_first_pos(&txn->hdr_idx); + + while ((cur_idx = txn->hdr_idx.v[cur_idx].next)) { + struct hdr_idx_elem *cur_hdr; + int val; + + cur_hdr = &txn->hdr_idx.v[cur_idx]; + cur_ptr = cur_next; + cur_end = cur_ptr + cur_hdr->len; + cur_next = cur_end + cur_hdr->cr + 1; + + /* We have one full header between cur_ptr and cur_end, and the + * next header starts at cur_next. + */ + + val = http_header_match2(cur_ptr, cur_end, "Pragma", 6); + if (val) { + if ((cur_end - (cur_ptr + val) >= 8) && + strncasecmp(cur_ptr + val, "no-cache", 8) == 0) { + pragma_found = 1; + continue; + } + } + + val = http_header_match2(cur_ptr, cur_end, "Cache-control", 13); + if (!val) + continue; + + /* OK, right now we know we have a cache-control header at cur_ptr */ + cc_found = 1; + p1 = cur_ptr + val; /* first non-space char after 'cache-control:' */ + + if (p1 >= cur_end) /* no more info */ + continue; + + /* p1 is at the beginning of the value */ + p2 = p1; + while (p2 < cur_end && *p2 != '=' && *p2 != ',' && !isspace((unsigned char)*p2)) + p2++; + + /* we have a complete value between p1 and p2. We don't check the + * values after max-age, max-stale nor min-fresh, we simply don't + * use the cache when they're specified. + */ + if (((p2 - p1 == 7) && strncasecmp(p1, "max-age", 7) == 0) || + ((p2 - p1 == 8) && strncasecmp(p1, "no-cache", 8) == 0) || + ((p2 - p1 == 9) && strncasecmp(p1, "max-stale", 9) == 0) || + ((p2 - p1 == 9) && strncasecmp(p1, "min-fresh", 9) == 0)) { + txn->flags |= TX_CACHE_IGNORE; + continue; + } + + if ((p2 - p1 == 8) && strncasecmp(p1, "no-store", 8) == 0) { + txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK; + continue; + } + } + + /* RFC7234#5.4: + * When the Cache-Control header field is also present and + * understood in a request, Pragma is ignored. + * When the Cache-Control header field is not present in a + * request, caches MUST consider the no-cache request + * pragma-directive as having the same effect as if + * "Cache-Control: no-cache" were present. + */ + if (!cc_found && pragma_found) + txn->flags |= TX_CACHE_IGNORE; +} + /* * Check if response is cacheable or not. Updates s->txn->flags. */