From e45729ec8afb120a61706e4f3b372b31336931a7 Mon Sep 17 00:00:00 2001 From: Thibaut Artis Date: Fri, 16 Jan 2026 11:56:50 +0100 Subject: [PATCH 1/3] cache: Introduce well_known_method field in struct http This commit introduces the 'wkm' field in struct http, this will allow us to evaluate requests' method once with strcmp and store the result in a bitmap, effectively transforming strcmp calls into binary AND operations. --- bin/varnishd/cache/cache.h | 7 ++++++ bin/varnishd/cache/cache_http.c | 2 ++ include/Makefile.am | 1 + include/tbl/well_known_methods.h | 40 ++++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 include/tbl/well_known_methods.h diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h index 5c397494004..f1a01c66f26 100644 --- a/bin/varnishd/cache/cache.h +++ b/bin/varnishd/cache/cache.h @@ -155,6 +155,12 @@ struct ws { * */ +enum well_known_method { +#define WKM(wk, bit) WKM_##wk = 1U << bit, +#include "tbl/well_known_methods.h" +#undef WKM +}; + struct http { unsigned magic; #define HTTP_MAGIC 0x6428b5c9 @@ -173,6 +179,7 @@ struct http { struct ws *ws; uint16_t status; uint8_t protover; + enum well_known_method wkm; }; /*--------------------------------------------------------------------*/ diff --git a/bin/varnishd/cache/cache_http.c b/bin/varnishd/cache/cache_http.c index f8402458f2b..252e5875c5a 100644 --- a/bin/varnishd/cache/cache_http.c +++ b/bin/varnishd/cache/cache_http.c @@ -361,6 +361,7 @@ HTTP_Setup(struct http *hp, struct ws *ws, struct vsl_log *vsl, hp->logtag = whence; hp->ws = ws; hp->vsl = vsl; + hp->wkm = WKM_UNKNOWN; } /*-------------------------------------------------------------------- @@ -397,6 +398,7 @@ HTTP_Dup(struct http *to, const struct http * fm) to->logtag = fm->logtag; to->status = fm->status; to->protover = fm->protover; + to->wkm = fm->wkm; } diff --git a/include/Makefile.am b/include/Makefile.am index 347724d70a8..ef4328f189d 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -48,6 +48,7 @@ nobase_pkginclude_HEADERS = \ tbl/vsl_tags.h \ tbl/vsl_tags_http.h \ tbl/waiters.h \ + tbl/well_known_methods.h \ vapi/vsc.h \ vapi/vsig.h \ vapi/vsl.h \ diff --git a/include/tbl/well_known_methods.h b/include/tbl/well_known_methods.h new file mode 100644 index 00000000000..d255ede36a2 --- /dev/null +++ b/include/tbl/well_known_methods.h @@ -0,0 +1,40 @@ +/*- + * Copyright 2026 UPLEX - Nils Goroll Systemoptimierung + * All rights reserved. + * + * Author: Thibaut Artis + * + * SPDX-License-Identifier: BSD-2-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Define well-known HTTP methods + */ + +WKM(GET, 0) +WKM(HEAD, 1) +WKM(POST, 2) +WKM(PUT, 3) +WKM(DELETE, 4) +WKM(OPTIONS, 5) +WKM(TRACE, 6) +WKM(PATCH, 7) +WKM(CONNECT, 8) +WKM(UNKNOWN, 31) From 2ca1d4f1fe2e19ee66f8d477772dffe27f8d9dea Mon Sep 17 00:00:00 2001 From: Thibaut Artis Date: Fri, 16 Jan 2026 11:58:33 +0100 Subject: [PATCH 2/3] cache: Set well_known_method on req parsing In this commit the http_SetWellKnownMethod is introduced and called on req/bereq.method initialization and modification. --- bin/varnishd/cache/cache.h | 1 + bin/varnishd/cache/cache_http.c | 26 ++++++++++++++++++++++++++ bin/varnishd/http1/cache_http1.h | 1 + bin/varnishd/http1/cache_http1_proto.c | 1 + 4 files changed, 29 insertions(+) diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h index f1a01c66f26..725e9476ee1 100644 --- a/bin/varnishd/cache/cache.h +++ b/bin/varnishd/cache/cache.h @@ -677,6 +677,7 @@ int HTTP_IterHdrPack(struct worker *, struct objcore *, const char **); const char *HTTP_GetHdrPack(struct worker *, struct objcore *, hdr_t); stream_close_t http_DoConnection(struct http *hp, stream_close_t sc_close); int http_IsFiltered(const struct http *hp, unsigned u, unsigned how); +void http_SetWellKnownMethod(struct http *hp); #define HTTPH_R_PASS (1 << 0) /* Request (c->b) in pass mode */ #define HTTPH_R_FETCH (1 << 1) /* Request (c->b) for fetch */ diff --git a/bin/varnishd/cache/cache_http.c b/bin/varnishd/cache/cache_http.c index 252e5875c5a..858d19b2afe 100644 --- a/bin/varnishd/cache/cache_http.c +++ b/bin/varnishd/cache/cache_http.c @@ -454,6 +454,8 @@ http_SetH(struct http *to, unsigned n, const char *header) http_VSLH(to, n); if (n == HTTP_HDR_PROTO) http_Proto(to); + if (n == HTTP_HDR_METHOD) + http_SetWellKnownMethod(to); } /*--------------------------------------------------------------------*/ @@ -1458,6 +1460,7 @@ http_FilterReq(struct http *to, const struct http *fm, unsigned how) CHECK_OBJ_NOTNULL(fm, HTTP_MAGIC); http_linkh(to, fm, HTTP_HDR_METHOD); + http_SetWellKnownMethod(to); http_linkh(to, fm, HTTP_HDR_URL); http_linkh(to, fm, HTTP_HDR_PROTO); to->protover = fm->protover; @@ -1627,3 +1630,26 @@ http_Unset(struct http *hp, hdr_t hdr) } hp->nhd = v; } + +void +http_SetWellKnownMethod(struct http *hp) +{ + size_t l; + const char *method; + + CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); + + method = http_GetMethod(hp); + AN(method); + hp->wkm = WKM_UNKNOWN; +#define WKM(wk, bit) \ + do { \ + l = vstrlen(method); \ + if (l == vstrlen(#wk) && !vstrcmp(#wk, method)) { \ + hp->wkm = WKM_##wk; \ + return; \ + } \ + } while (0); +#include "tbl/well_known_methods.h" +#undef WKM +} diff --git a/bin/varnishd/http1/cache_http1.h b/bin/varnishd/http1/cache_http1.h index d9b8f44057b..4a3b15ff783 100644 --- a/bin/varnishd/http1/cache_http1.h +++ b/bin/varnishd/http1/cache_http1.h @@ -68,3 +68,4 @@ stream_close_t V1L_Flush(struct v1l *v1l); stream_close_t V1L_Close(struct v1l **v1lp, uint64_t *cnt); size_t V1L_Write(struct v1l *v1l, const void *ptr, ssize_t len); extern const struct vdp * const VDP_v1l; +void http_SetWellKnownMethod(struct http *hp); diff --git a/bin/varnishd/http1/cache_http1_proto.c b/bin/varnishd/http1/cache_http1_proto.c index 54777278ecd..014b03e59b5 100644 --- a/bin/varnishd/http1/cache_http1_proto.c +++ b/bin/varnishd/http1/cache_http1_proto.c @@ -390,6 +390,7 @@ HTTP1_DissectRequest(struct http_conn *htc, struct http *hp) p = http_GetMethod(hp); AN(p); l = vstrlen(p); + http_SetWellKnownMethod(hp); if (htc->body_status == BS_EOF) { assert(hp->protover == 10); From 382ea77157290253a93203dc60a289925b7bebea Mon Sep 17 00:00:00 2001 From: Thibaut Artis Date: Fri, 16 Jan 2026 11:59:43 +0100 Subject: [PATCH 3/3] cache: Update http_method_eq to use well_known_method bitmap Here the http_method_eq macro is updated to use the http->wkm field in order to evaluate a request's method instead of vstrcmp(). This commit also introduces the http_method_among that allows checking whether a request's method can be found in a set of well known HTTP methods. --- bin/varnishd/cache/cache.h | 5 ++--- bin/varnishd/cache/cache_fetch.c | 11 ++--------- bin/varnishd/cache/cache_req_fsm.c | 2 +- bin/varnishd/http1/cache_http1_fetch.c | 2 +- bin/varnishd/http1/cache_http1_proto.c | 6 ++---- 5 files changed, 8 insertions(+), 18 deletions(-) diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h index 725e9476ee1..f51d7a5a0cd 100644 --- a/bin/varnishd/cache/cache.h +++ b/bin/varnishd/cache/cache.h @@ -702,9 +702,8 @@ extern hdr_t H__Reason; // rfc7230,l,1144,1144 // rfc7231,l,1156,1158 -#define http_method_eq(str, tok) (!vstrcmp(str, #tok)) -// l = vstrlen(str) -#define http_method_eq_l(str, l, tok) (l == vstrlen(#tok) && ! vstrcmp(str, #tok)) +#define http_method_eq(check, wkm) ((check & wkm) == wkm) +#define http_method_among(check, wkms) ((check & (wkms)) != 0) // rfc7230,l,1222,1222 // rfc7230,l,2848,2848 diff --git a/bin/varnishd/cache/cache_fetch.c b/bin/varnishd/cache/cache_fetch.c index d11ac66d70b..a216827b65b 100644 --- a/bin/varnishd/cache/cache_fetch.c +++ b/bin/varnishd/cache/cache_fetch.c @@ -382,8 +382,6 @@ vbf_stp_startfetch(struct worker *wrk, struct busyobj *bo) vtim_real now; unsigned handling, skip_vbr = 0; struct objcore *oc; - const char *met; - size_t met_l; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); @@ -407,13 +405,8 @@ vbf_stp_startfetch(struct worker *wrk, struct busyobj *bo) http_PrintfHeader(bo->bereq, "X-Varnish: %ju", VXID(bo->vsl->wid)); if (bo->bereq_body == NULL && bo->req == NULL) { - met = http_GetMethod(bo->bereq); - met_l = vstrlen(met); - if (http_method_eq_l(met, met_l, GET) || - http_method_eq_l(met, met_l, HEAD) || - http_method_eq_l(met, met_l, DELETE) || - http_method_eq_l(met, met_l, OPTIONS) || - http_method_eq_l(met, met_l, TRACE)) + if (http_method_among(bo->bereq->wkm, (WKM_GET | WKM_HEAD | WKM_DELETE | + WKM_OPTIONS | WKM_TRACE))) http_Unset(bo->bereq, H_Content_Length); else http_ForceHeader(bo->bereq, H_Content_Length, "0"); diff --git a/bin/varnishd/cache/cache_req_fsm.c b/bin/varnishd/cache/cache_req_fsm.c index f4d3810a272..dd1e20c27ef 100644 --- a/bin/varnishd/cache/cache_req_fsm.c +++ b/bin/varnishd/cache/cache_req_fsm.c @@ -452,7 +452,7 @@ cnt_transmit(struct worker *wrk, struct req *req) clval = http_GetContentLength(req->resp); /* RFC 7230, 3.3.3 */ status = http_GetStatus(req->resp); - head = http_method_eq(req->http0->hd[HTTP_HDR_METHOD].b, HEAD); + head = http_method_eq(req->http0->wkm, WKM_HEAD); if (req->boc != NULL || (req->objcore->flags & (OC_F_FAILED))) req->resp_len = clval; diff --git a/bin/varnishd/http1/cache_http1_fetch.c b/bin/varnishd/http1/cache_http1_fetch.c index f8bcf43cd49..a016cb79e04 100644 --- a/bin/varnishd/http1/cache_http1_fetch.c +++ b/bin/varnishd/http1/cache_http1_fetch.c @@ -272,7 +272,7 @@ V1F_FetchRespHdr(struct busyobj *bo) * Figure out how the fetch is supposed to happen, before the * headers are adulterated by VCL */ - if (http_method_eq(http_GetMethod(bo->bereq), HEAD)) { + if (http_method_eq(bo->bereq->wkm, WKM_HEAD)) { /* * A HEAD request can never have a body in the reply, * no matter what the headers might say. diff --git a/bin/varnishd/http1/cache_http1_proto.c b/bin/varnishd/http1/cache_http1_proto.c index 014b03e59b5..294f2bbc42b 100644 --- a/bin/varnishd/http1/cache_http1_proto.c +++ b/bin/varnishd/http1/cache_http1_proto.c @@ -355,7 +355,6 @@ HTTP1_DissectRequest(struct http_conn *htc, struct http *hp) uint16_t retval; const char *p; const char *b = NULL, *e; - size_t l; CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); @@ -389,19 +388,18 @@ HTTP1_DissectRequest(struct http_conn *htc, struct http *hp) p = http_GetMethod(hp); AN(p); - l = vstrlen(p); http_SetWellKnownMethod(hp); if (htc->body_status == BS_EOF) { assert(hp->protover == 10); /* RFC1945 8.3 p32 and D.1.1 p58 */ - if (http_method_eq_l(p, l, POST) || http_method_eq_l(p, l, PUT)) + if (http_method_among(hp->wkm, (WKM_POST | WKM_PUT))) return (400); htc->body_status = BS_NONE; } /* HEAD with a body is a hard error */ - if (htc->body_status != BS_NONE && http_method_eq_l(p, l, HEAD)) + if (htc->body_status != BS_NONE && http_method_eq(hp->wkm, WKM_HEAD)) return (400); return (retval);