Skip to content

Commit

Permalink
vmod_debug: demo a reembarking transport
Browse files Browse the repository at this point in the history
vmod_debug_transports.c demonstrates the basic mechanics of disembarking a
request from the vrt_deliver_f, delivering a body from another worker and
finishing the request.
  • Loading branch information
nigoroll committed Nov 3, 2024
1 parent 6d69209 commit 357f202
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 1 deletion.
39 changes: 39 additions & 0 deletions bin/varnishtest/tests/m00060.vtc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
varnishtest "VMOD debug reembarking transport"

server s1 {
rxreq
txresp -bodylen 131072
} -start

varnish v1 \
-vcl+backend {
import debug;

sub vcl_hash {
hash_data("");
return (lookup);
}

sub vcl_deliver {
if (req.url == "/chunked") {
set resp.filters = "debug.chunked";
}
debug.use_reembarking_http1();
}
} -start

varnish v1 -cliok "param.set debug +syncvsl"
varnish v1 -cliok "param.set debug +req_state"

client c1 -repeat 16 -keepalive {
txreq
rxresp
} -start

client c2 -repeat 16 -keepalive {
txreq -url "/chunked"
rxresp
} -start

client c1 -wait
client c2 -wait
3 changes: 2 additions & 1 deletion vmod/automake_boilerplate_debug.am
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ libvmod_debug_la_SOURCES = \
vmod_debug_acl.c \
vmod_debug_dyn.c \
vmod_debug_filters.c \
vmod_debug_obj.c
vmod_debug_obj.c \
vmod_debug_transports.c

libvmod_debug_la_CFLAGS =

Expand Down
7 changes: 7 additions & 0 deletions vmod/vmod_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ event_load(VRT_CTX, struct vmod_priv *priv)
priv->methods = priv_vcl_methods;

debug_add_filters(ctx);
debug_transport_init();
return (0);
}

Expand Down Expand Up @@ -1281,3 +1282,9 @@ xyzzy_resolve_range(VRT_CTX, struct VARGS(resolve_range) *args)
*(p.errp));
return (WS_VSB_finish(p.vsb, ctx->ws, NULL));
}

VCL_VOID
xyzzy_use_reembarking_http1(VRT_CTX)
{
debug_transport_use_reembarking_http1(ctx);
}
6 changes: 6 additions & 0 deletions vmod/vmod_debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,9 @@ void
debug_add_filters(VRT_CTX);
void
debug_remove_filters(VRT_CTX);

/* vmod_debug_transports.c */
void
debug_transport_use_reembarking_http1(VRT_CTX);
void
debug_transport_init(void);
7 changes: 7 additions & 0 deletions vmod/vmod_debug.vcc
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,13 @@ be hanged to zero. Any larger value will be taken modulo UINT32_MAX.

The *mode* argument behaves as for `debug.chksha256()`_.

$Function VOID use_reembarking_http1()

$Restrict vcl_deliver

Switch to the reembarking http1 debug transport. Calling it from any other
transport than http1 results in VCL failure.

DEPRECATED
==========

Expand Down
217 changes: 217 additions & 0 deletions vmod/vmod_debug_transports.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/*-
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006-2015 Varnish Software AS
* Copyright 2024 UPLEX - Nils Goroll Systemoptimierung
* All rights reserved.
*
* Authors: Poul-Henning Kamp <[email protected]>
* Nils Goroll <[email protected]>
*
* 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.
*/

#include "config.h"

#include "cache/cache_varnishd.h"

#include "cache/cache_filter.h"
#include "cache/cache_transport.h"
#include "http1/cache_http1.h"

#include "vmod_debug.h"

static void
dbg_error(struct req *req, const char *msg)
{

(void)req;
(void)msg;
INCOMPL();
}

static void dbg_deliver_finish(struct req *req, int err);
static void dbg_sendbody(struct worker *wrk, void *arg);

static task_func_t *hack_http1_req = NULL;

// copied from cache_http_deliver.c, then split & modified
static enum vtr_deliver_e v_matchproto_(vtr_deliver_f)
dbg_deliver(struct req *req, int sendbody)
{
struct vrt_ctx ctx[1];

CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
CHECK_OBJ_ORNULL(req->boc, BOC_MAGIC);
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);

if (req->doclose == SC_NULL &&
http_HdrIs(req->resp, H_Connection, "close")) {
req->doclose = SC_RESP_CLOSE;
} else if (req->doclose != SC_NULL) {
if (!http_HdrIs(req->resp, H_Connection, "close")) {
http_Unset(req->resp, H_Connection);
http_SetHeader(req->resp, "Connection: close");
}
} else if (!http_GetHdr(req->resp, H_Connection, NULL))
http_SetHeader(req->resp, "Connection: keep-alive");

if (sendbody) {
if (!http_GetHdr(req->resp, H_Content_Length, NULL)) {
if (req->http->protover == 11) {
http_SetHeader(req->resp,
"Transfer-Encoding: chunked");
} else {
req->doclose = SC_TX_EOF;
}
}
INIT_OBJ(ctx, VRT_CTX_MAGIC);
VCL_Req2Ctx(ctx, req);
if (VDP_Push(ctx, req->vdc, req->ws, VDP_v1l, NULL)) {
dbg_error(req, "Failure to push v1d processor");
return (VTR_D_DONE);
}
}

if (WS_Overflowed(req->ws)) {
dbg_error(req, "workspace_client overflow");
return (VTR_D_DONE);
}

if (WS_Overflowed(req->sp->ws)) {
dbg_error(req, "workspace_session overflow");
return (VTR_D_DONE);
}

V1L_Open(req->wrk, req->wrk->aws, &req->sp->fd, req->vsl,
req->t_prev + SESS_TMO(req->sp, send_timeout),
cache_param->http1_iovs);

if (WS_Overflowed(req->wrk->aws)) {
dbg_error(req, "workspace_thread overflow");
return (VTR_D_DONE);
}

req->acct.resp_hdrbytes += HTTP1_Write(req->wrk, req->resp, HTTP1_Resp);

if (! sendbody) {
dbg_deliver_finish(req, 0);
return (VTR_D_DONE);
}

(void)V1L_Flush(req->wrk);

if (hack_http1_req == NULL)
hack_http1_req = req->task->func;
AN(hack_http1_req);

VSLb(req->vsl, SLT_Debug, "w=%p scheduling dbg_sendbody", req->wrk);

req->task->func = dbg_sendbody;
req->task->priv = req;

req->transport_priv = req->wrk->v1l;
req->wrk->v1l = NULL;
req->wrk = NULL;
req->vdc->wrk = NULL;

AZ(Pool_Task(req->sp->pool, req->task, TASK_QUEUE_RUSH));
return (VTR_D_DISEMBARK);
}

static void v_matchproto_(task_func_t)
dbg_sendbody(struct worker *wrk, void *arg)
{
struct req *req;
const char *p;
int err, chunked;

CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);

THR_SetRequest(req);
VSLb(req->vsl, SLT_Debug, "w=%p enter dbg_sendbody", wrk);
AZ(req->wrk);
AZ(wrk->v1l);
CNT_Embark(wrk, req);
req->vdc->wrk = wrk; // move to CNT_Embark?
wrk->v1l = req->transport_priv;
req->transport_priv = NULL;
AN(wrk->v1l);

chunked = http_GetHdr(req->resp, H_Transfer_Encoding, &p) && strcmp(p, "chunked") == 0;
if (chunked)
V1L_Chunked(req->wrk);
err = VDP_DeliverObj(req->vdc, req->objcore);
if (!err && chunked)
V1L_EndChunk(req->wrk);
dbg_deliver_finish(req, err);

VSLb(req->vsl, SLT_Debug, "w=%p resuming http1_req", wrk);
wrk->task->func = hack_http1_req;
wrk->task->priv = req;
}

static void
dbg_deliver_finish(struct req *req, int err)
{
stream_close_t sc;
uint64_t bytes;

sc = V1L_Close(req->wrk, &bytes);
AZ(req->wrk->v1l);

req->acct.resp_bodybytes += VDP_Close(req->vdc, req->objcore, req->boc);

if (sc == SC_NULL && err && req->sp->fd >= 0)
sc = SC_REM_CLOSE;
if (sc != SC_NULL)
Req_Fail(req, sc);
}

struct transport DBG_transport;

void
debug_transport_init(void)
{
DBG_transport = HTTP1_transport;
DBG_transport.name = "DBG";
DBG_transport.deliver = dbg_deliver;
}

void
debug_transport_use_reembarking_http1(VRT_CTX)
{
struct req *req;

CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
req = ctx->req;
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);

if (req->transport != &HTTP1_transport) {
VRT_fail(ctx, "Only works on built-in http1 transport");
return;
}
AZ(req->transport_priv);
req->transport = &DBG_transport;
}

0 comments on commit 357f202

Please sign in to comment.