diff --git a/bin/varnishtest/tests/s00013.vtc b/bin/varnishtest/tests/s00013.vtc new file mode 100644 index 00000000000..25429177175 --- /dev/null +++ b/bin/varnishtest/tests/s00013.vtc @@ -0,0 +1,111 @@ +varnishtest "partial std.cache_req_body()" + +barrier b1 sock 2 +barrier b2 sock 2 + +server s1 { + # chunked with retry + close + + accept + rxreq + expect req.http.transfer-encoding == chunked + expect req.bodylen == 150 + txresp + expect_close + + # straight with retry + accept + close + + accept + rxreq + expect req.http.content-length == 19 + expect req.bodylen == 19 + expect req.body == "hello partial world" + txresp + expect_close + + # chunked pipe + accept + rxreq + expect req.http.transfer-encoding == chunked + expect req.bodylen == 150 + txresp + close + + # straight pipe + accept + rxreq + expect req.http.content-length == 19 + expect req.bodylen == 19 + expect req.body == "hello partial world" + txresp +} -start + +varnish v1 -cliok "param.set debug +syncvsl,+flush_head,+slow_bereq" +varnish v1 -vcl+backend { + import std; + import vtc; + + sub vcl_recv { + std.cache_req_body(10b, partial=true); + } + + sub vcl_backend_fetch { + if (bereq.retries == 1) { + vtc.barrier_sync(bereq.http.barrier); + } + set bereq.http.connection = "close"; + } + + sub vcl_backend_error { + return (retry); + } +} -start + +logexpect l1 -v v1 -i FetchError -g raw { + expect 0 1002 FetchError "Failed to send a partial req.body" + expect 0 1002 FetchError "backend write error" +} -start + +client c1 { + txreq -method POST -nolen -hdr "transfer-encoding: chunked" \ + -hdr "barrier: ${b1_sock}" + chunkedlen 100 + barrier b1 sync + chunkedlen 50 + chunkedlen 0 + rxresp + expect resp.status == 200 + expect resp.http.x-varnish == 1001 + + txreq -method POST -nolen -hdr "content-length: 19" \ + -hdr "barrier: ${b2_sock}" + send "hello partial " + barrier b2 sync + send "world" + rxresp + expect resp.status == 200 + expect resp.http.x-varnish == 1004 + + txreq -method PIPE -nolen -hdr "transfer-encoding: chunked" + chunkedlen 100 + chunkedlen 50 + chunkedlen 0 + rxresp + expect resp.status == 200 + expect resp.http.x-varnish == + expect_close +} -run + +client c2 { + txreq -method PIPE -nolen -hdr "content-length: 19" + send "hello partial world" + rxresp + expect resp.status == 200 + expect resp.http.x-varnish == +} -run + +logexpect l1 -wait +server s1 -wait diff --git a/vmod/vmod_std.c b/vmod/vmod_std.c index fe20c202530..a21cb5b6756 100644 --- a/vmod/vmod_std.c +++ b/vmod/vmod_std.c @@ -239,11 +239,11 @@ vmod_timestamp(VRT_CTX, VCL_STRING label) } VCL_BOOL v_matchproto_(td_std_cache_req_body) -vmod_cache_req_body(VRT_CTX, VCL_BYTES size) +vmod_cache_req_body(VRT_CTX, VCL_BYTES size, VCL_BOOL partial) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); size = vmax_t(VCL_BYTES, size, 0); - if (VRT_CacheReqBody(ctx, (size_t)size, 0) < 0) + if (VRT_CacheReqBody(ctx, (size_t)size, partial) < 0) return (0); return (1); } diff --git a/vmod/vmod_std.vcc b/vmod/vmod_std.vcc index acd273c884e..4be0e789c09 100644 --- a/vmod/vmod_std.vcc +++ b/vmod/vmod_std.vcc @@ -505,14 +505,18 @@ Example:: set req.http.My-Env = std.getenv("MY_ENV"); -$Function BOOL cache_req_body(BYTES size) +$Function BOOL cache_req_body(BYTES size, BOOL partial = 0) Caches the request body if it is smaller than *size*. Returns ``true`` if the body was cached, ``false`` otherwise. Normally the request body can only be sent once. Caching it enables retrying backend requests with a request body, as usually the case -with ``POST`` and ``PUT``. +with ``POST`` and ``PUT``. When the ``partial`` argument is ``true``, +the request body may be larger than ``size`` and only up to ``size`` +bytes may be cached. This also enables retrying backend requests with +a request body, unless request body bytes were consumed beyond the +partial cached payload. Example::