diff --git a/bin/varnishd/Makefile.am b/bin/varnishd/Makefile.am index 0d60f1b188..8ccfe1909e 100644 --- a/bin/varnishd/Makefile.am +++ b/bin/varnishd/Makefile.am @@ -109,6 +109,7 @@ varnishd_SOURCES = \ storage/storage_malloc.c \ storage/storage_debug.c \ storage/storage_simple.c \ + storage/storage_synth.c \ storage/storage_umem.c \ waiter/cache_waiter.c \ waiter/cache_waiter_epoll.c \ diff --git a/bin/varnishd/cache/cache_req.c b/bin/varnishd/cache/cache_req.c index 2fa6ecd773..ec4578f73f 100644 --- a/bin/varnishd/cache/cache_req.c +++ b/bin/varnishd/cache/cache_req.c @@ -39,8 +39,10 @@ #include "cache_varnishd.h" #include "cache_filter.h" +#include "cache_objhead.h" #include "cache_pool.h" #include "cache_transport.h" +#include "storage/storage.h" // stv_default #include "common/heritage.h" #include "vtim.h" @@ -325,6 +327,39 @@ Req_Cleanup(struct sess *sp, struct worker *wrk, struct req *req) VDP_Fini(req->vdc); } +/*---------------------------------------------------------------------- + * response body for vcl_synth {} + */ + +static void +resp_u_storage(struct req *req) +{ + + CHECK_OBJ_NOTNULL(req, REQ_MAGIC); + HSH_DerefBoc(req->wrk, req->objcore); + (void)HSH_DerefObjCore(req->wrk, &req->objcore, 0); +} + +int +Resp_l_storage(struct req *req, const struct stevedore *stv) +{ + int r; + + CHECK_OBJ_NOTNULL(req, REQ_MAGIC); + CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC); + + if (req->objcore != NULL) + resp_u_storage(req); + AZ(req->objcore); + req->objcore = HSH_Private(req->wrk); + CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); + r = STV_NewObject(req->wrk, req->objcore, stv, 0); + if (r == 0) + resp_u_storage(req); + return (r); +} + + /*---------------------------------------------------------------------- */ diff --git a/bin/varnishd/cache/cache_req_fsm.c b/bin/varnishd/cache/cache_req_fsm.c index 803810210e..49797eca53 100644 --- a/bin/varnishd/cache/cache_req_fsm.c +++ b/bin/varnishd/cache/cache_req_fsm.c @@ -381,11 +381,11 @@ cnt_synth(struct worker *wrk, struct req *req) /* Discard any lingering request body before delivery */ (void)VRB_Ignore(req); - - req->objcore = HSH_Private(wrk); - CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); szl = -1; - if (STV_NewObject(wrk, req->objcore, stv_transient, 0)) { + + if (req->objcore != NULL || + Resp_l_storage(req, stv_transient)) { + CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); body = VSB_data(synth_body); szl = VSB_len(synth_body); assert(szl >= 0); @@ -406,14 +406,16 @@ cnt_synth(struct worker *wrk, struct req *req) if (szl >= 0) AZ(ObjSetU64(wrk, req->objcore, OA_LEN, VSB_len(synth_body))); - HSH_DerefBoc(wrk, req->objcore); + if (req->objcore != NULL) + HSH_DerefBoc(wrk, req->objcore); VSB_destroy(&synth_body); if (szl < 0) { VSLb(req->vsl, SLT_Error, "Could not get storage"); req->doclose = SC_OVERLOAD; VSLb_ts_req(req, "Resp", W_TIM_real(wrk)); - (void)HSH_DerefObjCore(wrk, &req->objcore, 1); + if (req->objcore != NULL) + (void)HSH_DerefObjCore(wrk, &req->objcore, 1); http_Teardown(req->resp); return (REQ_FSM_DONE); } diff --git a/bin/varnishd/cache/cache_varnishd.h b/bin/varnishd/cache/cache_varnishd.h index 2892ef188d..67486ca1dc 100644 --- a/bin/varnishd/cache/cache_varnishd.h +++ b/bin/varnishd/cache/cache_varnishd.h @@ -428,6 +428,7 @@ void Req_Fail(struct req *req, stream_close_t reason); void Req_AcctLogCharge(struct VSC_main_wrk *, struct req *); void Req_LogHit(struct worker *, struct req *, struct objcore *, intmax_t); const char *Req_LogStart(const struct worker *, struct req *); +int Resp_l_storage(struct req *req, const struct stevedore *stv); /* cache_req_body.c */ int VRB_Ignore(struct req *); diff --git a/bin/varnishd/cache/cache_vrt_var.c b/bin/varnishd/cache/cache_vrt_var.c index 24c69cbfc2..3ff4518823 100644 --- a/bin/varnishd/cache/cache_vrt_var.c +++ b/bin/varnishd/cache/cache_vrt_var.c @@ -37,6 +37,7 @@ #include "cache_varnishd.h" #include "cache_objhead.h" #include "cache_transport.h" +#include "storage/storage.h" #include "common/heritage.h" #include "vcl.h" @@ -473,6 +474,34 @@ VRT_l_req_storage(VRT_CTX, VCL_STEVEDORE stv) /*--------------------------------------------------------------------*/ +VCL_STEVEDORE +VRT_r_resp_storage(VRT_CTX) +{ + struct objcore *oc; + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); + oc = ctx->req->objcore; + if (oc == NULL) + VRT_l_resp_storage(ctx, NULL); + oc = ctx->req->objcore; + CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); + return (oc->stobj->stevedore); +} + +VCL_VOID +VRT_l_resp_storage(VRT_CTX, VCL_STEVEDORE stv) +{ + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + if (stv == NULL) + stv = stv_transient; + if (! Resp_l_storage(ctx->req, stv)) + VRT_fail(ctx, "Storage %s failed", stv->vclname); +} + +/*--------------------------------------------------------------------*/ + VCL_STEVEDORE VRT_r_beresp_storage(VRT_CTX) { @@ -493,8 +522,6 @@ VRT_l_beresp_storage(VRT_CTX, VCL_STEVEDORE stv) * VCL <= 4.0 ONLY */ -#include "storage/storage.h" - VCL_STRING VRT_r_beresp_storage_hint(VRT_CTX) { @@ -1093,7 +1120,79 @@ VRT_l_##which##_body(VRT_CTX, enum lbody_e type, \ } VRT_BODY_L(beresp) -VRT_BODY_L(resp) +static VRT_BODY_L(resp_vsb) + +VCL_VOID +VRT_l_resp_body(VRT_CTX, enum lbody_e type, + const char *str, VCL_BODY body) +{ + struct vscarab *scarab; + struct viov *viov; + struct req *req; + ssize_t sz = 0; + VCL_STRANDS s; + VCL_BLOB b; + int n; + + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + AN(body); + req = ctx->req; + CHECK_OBJ_NOTNULL(req, REQ_MAGIC); + + if (req->objcore != NULL && + req->objcore->stobj->stevedore != &ssy_stevedore) { + VRT_l_resp_vsb_body(ctx, type, str, body); + return; + } + + if (req->objcore == NULL || + type == LBODY_SET_STRING || type == LBODY_SET_BLOB) + VRT_l_resp_storage(ctx, &ssy_stevedore); + + if (! ObjGetSpace(req->wrk, req->objcore, &sz, (uint8_t **)(void*)&scarab)) { + VRT_fail(ctx, "Synth storage failed"); + return; + } + + VSCARAB_CHECK_NOTNULL(scarab); + + if (type == LBODY_SET_BLOB || type == LBODY_ADD_BLOB) { + AZ(str); + b = body; + viov = VSCARAB_GET(scarab); + AN(viov); // ObjGetSpace ensures + viov->iov.iov_base = TRUST_ME(b->blob); + viov->iov.iov_len = b->len; + return; + } + if (str != NULL) { + viov = VSCARAB_GET(scarab); + AN(viov); // ObjGetSpace ensures + viov->iov.iov_base = TRUST_ME(str); + viov->iov.iov_len = strlen(str); + } + + s = body; + for (n = 0; s != NULL && n < s->n; n++) { + if (s->p[n] == NULL || *s->p[n] == '\0') + continue; + + viov = VSCARAB_GET(scarab); + if (viov == NULL) { + if (! ObjGetSpace(req->wrk, req->objcore, &sz, + (uint8_t **)(void*)&scarab)) { + VRT_fail(ctx, "Synth storage failed"); + return; + } + viov = VSCARAB_GET(scarab); + } + AN(viov); + viov->iov.iov_base = TRUST_ME(s->p[n]); + viov->iov.iov_len = strlen(s->p[n]); + } +} + /*--------------------------------------------------------------------*/ diff --git a/bin/varnishd/storage/mgt_stevedore.c b/bin/varnishd/storage/mgt_stevedore.c index b62248365f..e443dd3c5a 100644 --- a/bin/varnishd/storage/mgt_stevedore.c +++ b/bin/varnishd/storage/mgt_stevedore.c @@ -183,6 +183,7 @@ STV_Register_The_Usual_Suspects(void) STV_Register(&smf_stevedore, NULL); STV_Register(&sma_stevedore, NULL); STV_Register(&smd_stevedore, NULL); + STV_Register(&ssy_stevedore, NULL); #ifdef WITH_PERSISTENT_STORAGE STV_Register(&smp_stevedore, NULL); STV_Register(&smp_fake_stevedore, NULL); diff --git a/bin/varnishd/storage/storage.h b/bin/varnishd/storage/storage.h index b030f2c54d..82c9862113 100644 --- a/bin/varnishd/storage/storage.h +++ b/bin/varnishd/storage/storage.h @@ -169,6 +169,7 @@ void LRU_Touch(struct worker *, struct objcore *, vtim_real now); /*--------------------------------------------------------------------*/ extern const struct stevedore smu_stevedore; extern const struct stevedore sma_stevedore; +extern const struct stevedore ssy_stevedore; extern const struct stevedore smd_stevedore; extern const struct stevedore smf_stevedore; extern const struct stevedore smp_stevedore; diff --git a/bin/varnishd/storage/storage_simple.c b/bin/varnishd/storage/storage_simple.c index 4ddab3590e..80f19cd79e 100644 --- a/bin/varnishd/storage/storage_simple.c +++ b/bin/varnishd/storage/storage_simple.c @@ -736,8 +736,8 @@ sml_notify_wait(struct sml_notify *sn) AZ(pthread_mutex_unlock(&sn->mtx)); } -static int v_matchproto_(objiterator_f) -sml_iterator(struct worker *wrk, struct objcore *oc, +int v_matchproto_(objiterator_f) +SML_iterator(struct worker *wrk, struct objcore *oc, void *priv, objiterate_f *func, int final) { struct sml_notify sn; @@ -1144,7 +1144,7 @@ sml_setattr(struct worker *wrk, struct objcore *oc, enum obj_attr attr, const struct obj_methods SML_methods = { .objfree = sml_objfree, - .objiterator = sml_iterator, + .objiterator = SML_iterator, .objgetspace = sml_getspace, .objextend = sml_extend, .objtrimstore = sml_trimstore, diff --git a/bin/varnishd/storage/storage_simple.h b/bin/varnishd/storage/storage_simple.h index 207a11505a..82373eba16 100644 --- a/bin/varnishd/storage/storage_simple.h +++ b/bin/varnishd/storage/storage_simple.h @@ -87,6 +87,8 @@ struct object *SML_MkObject(const struct stevedore *, struct objcore *, void *SML_AllocBuf(struct worker *, const struct stevedore *, size_t, uintptr_t *); void SML_FreeBuf(struct worker *, const struct stevedore *, uintptr_t); +int SML_iterator(struct worker *wrk, struct objcore *oc, + void *priv, objiterate_f *func, int final); storage_allocobj_f SML_allocobj; storage_panic_f SML_panic; diff --git a/bin/varnishd/storage/storage_synth.c b/bin/varnishd/storage/storage_synth.c new file mode 100644 index 0000000000..d7b7a85a21 --- /dev/null +++ b/bin/varnishd/storage/storage_synth.c @@ -0,0 +1,369 @@ +/*- + * Copyright 2025 UPLEX - Nils Goroll Systemoptimierung + * All rights reserved. + * + * Author: Nils Goroll + * + * 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. + * + * Special-purpose stevedore for use with transient synth objects as those + * created from vcl_synth {}: Does not copy, only references on-workspace + * data + */ + +#include "config.h" + +#include + +#include "cache/cache_varnishd.h" + +#include "cache/cache_obj.h" +#include "cache/cache_objhead.h" + +#include "storage/storage.h" +#include "storage/storage_simple.h" + +#include "vend.h" + +struct ssy_st { + unsigned magic; +#define SSY_ST_MAGIC 0x236918b3 + VSTAILQ_ENTRY(ssy_st) list; + struct vscarab scarab; +}; + +//lint -emacro({413}, SSY_ST_SIZE) +#define SSY_ST_SIZE(cap) (offsetof(struct ssy_st, scarab) + VSCARAB_SIZE(cap)) + +struct ssy { + unsigned magic; +#define SSY_MAGIC 0xfb41f362 + unsigned flags; +#define SSY_F_LEN 1 + struct ws *ws; + VSTAILQ_HEAD(,ssy_st) head; + // getattr needs to return *pointer* to len as big endian + uint8_t len_be[sizeof(uint64_t)]; +}; + +static void v_matchproto_(objfree_f) +ssy_objfree(struct worker *wrk, struct objcore *oc) +{ + + CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); + // object is on workspace, nothing to free here + CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); + memset(oc->stobj, 0, sizeof *oc->stobj); + wrk->stats->n_object--; +} + +static struct ssy_st * +ssy_st_alloc(struct ssy *ssy) +{ + struct ssy_st *st; + const size_t cap = 8; // XXX GOOD? + const size_t sz = SSY_ST_SIZE(cap); + + CHECK_OBJ_NOTNULL(ssy, SSY_MAGIC); + st = WS_Alloc(ssy->ws, sz); + if (st == NULL) + return (st); + INIT_OBJ(st, SSY_ST_MAGIC); + VSCARAB_INIT(&st->scarab, cap); + VSTAILQ_INSERT_TAIL(&ssy->head, st, list); + return (st); +} + +/* + * deliberate API violation: does not return space, but a VSCARAB + * + * XXX own stevedore method? + */ +static int v_matchproto_(objgetspace_f) +ssy_getspace(struct worker *wrk, struct objcore *oc, ssize_t *sz, + uint8_t **ptr) +{ + struct ssy *ssy; + struct ssy_st *st; + + CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); + CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); + CAST_OBJ_NOTNULL(ssy, oc->stobj->priv, SSY_MAGIC); + (void) sz; + + st = VSTAILQ_LAST(&ssy->head, ssy_st, list); + CHECK_OBJ_NOTNULL(st, SSY_ST_MAGIC); + CHECK_OBJ_NOTNULL(&st->scarab, VSCARAB_MAGIC); + + if (st->scarab.used == st->scarab.capacity) { + st = ssy_st_alloc(ssy); + if (st == NULL) + return (0); + } + *ptr = (void*)(&st->scarab); + return (1); +} + +/* + * VAI for ssy is really simple because we never block, hence need no + * notification + */ + +struct ssy_hdl { + struct vai_hdl_preamble preamble; +#define SSY_HDL_MAGIC 0x07a0be62 + //struct ssy *ssy; + struct ssy_st *st; + struct viov *viov; +}; + +static int +ssy_ai_lease(struct worker *wrk, vai_hdl vhdl, struct vscarab *dst) +{ + struct ssy_hdl *hdl; + struct viov *dviov; + int r = 0; + + (void) wrk; + CAST_VAI_HDL_NOTNULL(hdl, vhdl, SSY_HDL_MAGIC); + VSCARAB_CHECK_NOTNULL(dst); + + if (hdl->st == NULL) { + dst->flags |= VSCARAB_F_END; + return (r); + } + + CHECK_OBJ(hdl->st, SSY_ST_MAGIC); + VSCARAB_CHECK(&hdl->st->scarab); + AN(hdl->viov); + + do { + VSCARAB_FOREACH_RESUME(hdl->viov, &hdl->st->scarab) { + dviov = VSCARAB_GET(dst); + if (dviov == NULL) + break; + *dviov = *hdl->viov; + dviov->lease = VAI_LEASE_NORET; + r++; + } + if (hdl->viov == NULL) { + hdl->st = VSTAILQ_NEXT(hdl->st, list); + if (hdl->st == NULL) + dst->flags |= VSCARAB_F_END; + else + hdl->viov = &hdl->st->scarab.s[0]; + } + } while (hdl->viov != NULL); + + return (r); +} + +// just malloc +static int +ssy_ai_buffer(struct worker *wrk, vai_hdl vhdl, struct vscarab *scarab) +{ + struct ssy_hdl *hdl; + struct viov *vio; + void *p; + int r = 0; + + (void) wrk; + CAST_VAI_HDL_NOTNULL(hdl, vhdl, SSY_HDL_MAGIC); + + VSCARAB_FOREACH(vio, scarab) + if (vio->iov.iov_len > UINT_MAX) + return (-EINVAL); + + VSCARAB_FOREACH(vio, scarab) { + p = malloc(vio->iov.iov_len); + AN(p); + vio->iov.iov_base = p; + vio->lease = ptr2lease(p); + r++; + } + return (r); +} + +// just free buffers +static void v_matchproto_(vai_return_f) +ssy_ai_return(struct worker *wrk, vai_hdl vhdl, struct vscaret *scaret) +{ + struct ssy_hdl *hdl; + uint64_t *p; + + (void) wrk; + CAST_VAI_HDL_NOTNULL(hdl, vhdl, SSY_HDL_MAGIC); + VSCARET_CHECK_NOTNULL(scaret); + VSCARET_FOREACH(p, scaret) { + if (*p != VAI_LEASE_NORET) + free (lease2ptr(*p)); + } +} + +static void v_matchproto_(vai_fini_f) +ssy_ai_fini(struct worker *wrk, vai_hdl *vai_hdlp) +{ + struct ssy_hdl *hdl; + + (void)wrk; + AN(vai_hdlp); + CAST_VAI_HDL_NOTNULL(hdl, *vai_hdlp, SSY_HDL_MAGIC); + *vai_hdlp = NULL; +} + +static void * v_matchproto_(objsetattr_f) +ssy_setattr(struct worker *wrk, struct objcore *oc, enum obj_attr attr, + ssize_t len, const void *ptr) +{ + static uint64_t ignored; + + (void)wrk; + (void)oc; + assert(attr == OA_LEN); + (void)len; + (void)ptr; + return (&ignored); +} + +static const void * v_matchproto_(objgetattr_f) +ssy_getattr(struct worker *wrk, struct objcore *oc, enum obj_attr attr, + ssize_t *len) +{ + struct ssy *ssy; + struct ssy_st *st; + struct viov *viov; + static const uint8_t flags = 0; + uint64_t l = 0; + + (void)wrk; + CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); + + if (attr == OA_FLAGS) { + AZ(len); + return (&flags); + } + + if (attr != OA_LEN) + return (NULL); + + AN(len); + CAST_OBJ_NOTNULL(ssy, oc->stobj->priv, SSY_MAGIC); + assert(attr == OA_LEN); + + if ((ssy->flags & SSY_F_LEN) == 0) { + VSTAILQ_FOREACH(st, &ssy->head, list) { + VSCARAB_FOREACH(viov, &st->scarab) + l += viov->iov.iov_len; + } + vbe64enc(ssy->len_be, l); + ssy->flags |= SSY_F_LEN; + } + *len = sizeof ssy->len_be; + return (ssy->len_be); +} + +static vai_hdl v_matchproto_(vai_init_f) +ssy_ai_init(struct worker *wrk, struct objcore *oc, struct ws *ws, + vai_notify_cb *notify, void *notify_priv) +{ + struct ssy_hdl *hdl; + struct ssy *ssy; + + (void)wrk; + CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); + CAST_OBJ_NOTNULL(ssy, oc->stobj->priv, SSY_MAGIC); + (void)notify; + (void)notify_priv; + + if (ws == NULL) + ws = ssy->ws; + hdl = WS_Alloc(ws, sizeof *hdl); + if (hdl == NULL) + return (NULL); + + INIT_VAI_HDL(hdl, SSY_HDL_MAGIC); + hdl->st = VSTAILQ_FIRST(&ssy->head); + CHECK_OBJ(hdl->st, SSY_ST_MAGIC); + VSCARAB_CHECK(&hdl->st->scarab); + hdl->viov = &hdl->st->scarab.s[0]; + + AN(hdl); + hdl->preamble.vai_lease = ssy_ai_lease; + hdl->preamble.vai_buffer = ssy_ai_buffer; + hdl->preamble.vai_return = ssy_ai_return; + hdl->preamble.vai_fini = ssy_ai_fini; + return (hdl); +} + +static const struct obj_methods ssy_methods = { + .objfree = ssy_objfree, + .objiterator = SML_iterator, + .objgetspace = ssy_getspace, + .objextend = NULL, // don't call, asserts in cache_obj.c + .objtrimstore = NULL, + .objbocdone = NULL, + .objslim = NULL, + .objgetattr = ssy_getattr, // only OA_LEN, OA_FLAGS, others not + .objsetattr = ssy_setattr, // only OA_LEN, ignores value + .objtouch = NULL, + .vai_init = ssy_ai_init +}; + +static int v_matchproto_(storage_allocobj_f) +ssy_allocobj(struct worker *wrk, const struct stevedore *stv, + struct objcore *oc, unsigned wsl) +{ + struct req *req; + struct ssy *ssy; + + CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); + CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC); + CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); + AZ(wsl); + + // XXX API? + req = THR_GetRequest(); + CHECK_OBJ_NOTNULL(req, REQ_MAGIC); + + ssy = WS_Alloc(req->ws, sizeof *ssy); + if (ssy == NULL) + return (0); + INIT_OBJ(ssy, SSY_MAGIC); + ssy->ws = req->ws; + VSTAILQ_INIT(&ssy->head); + if (ssy_st_alloc(ssy) == NULL) + return (0); + oc->stobj->stevedore = stv; + oc->stobj->priv = ssy; + return (1); +} + +const struct stevedore ssy_stevedore = { + .magic = STEVEDORE_MAGIC, + .name = "synth", + .vclname = "storage.synth", + .ident = "synth", + .allocobj = ssy_allocobj, + .methods = &ssy_methods +}; diff --git a/bin/varnishtest/tests/b00017.vtc b/bin/varnishtest/tests/b00017.vtc index b205a80644..65d6a17589 100644 --- a/bin/varnishtest/tests/b00017.vtc +++ b/bin/varnishtest/tests/b00017.vtc @@ -1,6 +1,6 @@ -varnishtest "Check set resp.body in vcl_synth" +varnishtest "Check set resp.body & storage in vcl_synth" -varnish v1 -arg "-sTransient=debug,lessspace" -vcl { +varnish v1 -arg "-sTransient=debug,lessspace -ssynth=synth" -vcl { backend foo { .host = "${bad_backend}"; } @@ -9,7 +9,11 @@ varnish v1 -arg "-sTransient=debug,lessspace" -vcl { } sub vcl_synth { - set resp.body = "Custom vcl_synth's body"; + if (! req.http.synth) { + set resp.storage = storage.Transient; + } + set resp.body = "Custom vcl_synth's " + now + " body"; + set resp.http.storage = resp.storage; return (deliver); } } -start @@ -19,8 +23,19 @@ client c1 { rxresp expect resp.status == 888 expect resp.http.connection != close - expect resp.bodylen == 23 - expect resp.body == "Custom vcl_synth's body" + expect resp.bodylen >= 52 + expect resp.bodylen <= 53 + expect resp.body ~ "^Custom vcl_synth's .* body$" + expect resp.http.storage == "storage.Transient" + + txreq -url "/" -hdr "synth: true" + rxresp + expect resp.status == 888 + expect resp.http.connection != close + expect resp.bodylen >= 52 + expect resp.bodylen <= 53 + expect resp.body ~ "^Custom vcl_synth's .* body$" + expect resp.http.storage == "storage.synth" } -run -varnish v1 -expect s_synth == 1 +varnish v1 -expect s_synth == 2 diff --git a/bin/varnishtest/tests/r01284.vtc b/bin/varnishtest/tests/r01284.vtc index e188e69a5d..f0c57b6f40 100644 --- a/bin/varnishtest/tests/r01284.vtc +++ b/bin/varnishtest/tests/r01284.vtc @@ -20,6 +20,9 @@ varnish v1 \ # Unset Date header to not change the object sizes unset beresp.http.Date; } + sub vcl_synth { + set resp.storage = storage.Transient; + } } -start varnish v1 -cliok "param.set debug +syncvsl" @@ -45,7 +48,7 @@ client c1 { delay 1 } -run -# Three failures, one for obj2, one for vcl_backend_error{}, one for synth objcore +# Two failures, one for obj2, one for vcl_backend_error{}, one for synth objcore varnish v1 -expect SM?.Transient.c_fail == 3 client c1 { diff --git a/bin/varnishtest/tests/r03502.vtc b/bin/varnishtest/tests/r03502.vtc index 9076e64df9..866b38034c 100644 --- a/bin/varnishtest/tests/r03502.vtc +++ b/bin/varnishtest/tests/r03502.vtc @@ -52,8 +52,8 @@ client c1 { expect resp.status == 200 txreq -hdr "Accept-Encoding: gzip" - # no storage for synth either - expect_close + rxresp + expect resp.status == 503 } -run logexpect l1 -wait diff --git a/doc/sphinx/reference/vcl_var.rst b/doc/sphinx/reference/vcl_var.rst index 6b724ebb8d..e0415e6f30 100644 --- a/doc/sphinx/reference/vcl_var.rst +++ b/doc/sphinx/reference/vcl_var.rst @@ -1776,6 +1776,24 @@ resp.status response which created the object. If the object has not been modified based on that comparison, a 304 is sent. +.. _resp.storage: + +resp.storage + + Type: STEVEDORE + + Readable from: vcl_synth + + Writable from: vcl_synth + + + The storage backend to use for the synthetic response created in + vcl_synth. + + Changing resp.storage deletes any synthetic body data as if ``unset + resp.body`` had been called. + + Defaults to storage.Transient .. _resp.time: