Commit e2756ace authored by Nils Goroll's avatar Nils Goroll

basic support for scope=TOP digests

parent 4b12af66
Pipeline #358 skipped
......@@ -26,7 +26,7 @@ import blobdigest [from "path"] ;
::
new OBJECT = blobdigest.digest(ENUM hash [, BLOB init])
new OBJECT = blobdigest.digest(ENUM hash [, BLOB init], ["TASK"|"TOP"])
BOOL <obj>.update(BLOB)
BLOB <obj>.final()
......@@ -202,7 +202,7 @@ values:
CONTENTS
========
* digest(ENUM {CRC32,MD5,SHA1,SHA224,SHA256,SHA384,SHA512,SHA3_224,SHA3_256,SHA3_384,SHA3_512}, BLOB)
* digest(ENUM {CRC32,MD5,SHA1,SHA224,SHA256,SHA384,SHA512,SHA3_224,SHA3_256,SHA3_384,SHA3_512}, BLOB, ENUM {TASK,TOP})
* BLOB hash(ENUM {CRC32,MD5,SHA1,SHA224,SHA256,SHA384,SHA512,SHA3_224,SHA3_256,SHA3_384,SHA3_512}, BLOB)
* hmac(ENUM {MD5,SHA1,SHA224,SHA256,SHA384,SHA512,SHA3_224,SHA3_256,SHA3_384,SHA3_512}, BLOB)
* BLOB hmacf(ENUM {MD5,SHA1,SHA224,SHA256,SHA384,SHA512,SHA3_224,SHA3_256,SHA3_384,SHA3_512}, BLOB, BLOB)
......@@ -215,7 +215,7 @@ digest
::
new OBJ = digest(ENUM {CRC32,MD5,SHA1,SHA224,SHA256,SHA384,SHA512,SHA3_224,SHA3_256,SHA3_384,SHA3_512} hash, BLOB init=0)
new OBJ = digest(ENUM {CRC32,MD5,SHA1,SHA224,SHA256,SHA384,SHA512,SHA3_224,SHA3_256,SHA3_384,SHA3_512} hash, BLOB init=0, ENUM {TASK,TOP} scope="TASK")
Initialize a message digest context for the algorithm ``hash``, and
optionally update it with ``init``. If ``init`` is left out, then an
......@@ -225,6 +225,13 @@ If an ``init`` BLOB is provided, then the message digests computed
from this object result with ``init`` prepended before any BLOBs added
by the ``.update()`` method.
By default, the scope of the message digest context is ``TASK``:
method calls affect separate context states for the client and backend
side.
``TOP`` scope provides a context across all ESI subrequests, but is
available only on the client side.
Example::
import blobdigest;
......
# looks like -*- vcl -*-
varnishtest "digest scope=TOP"
server s1 {
rxreq
txresp -body {
<html>
Before include
<esi:include src="/body" sr="foo"/>
After include
</html>
}
rxreq
expect req.url == "/body1"
txresp -body {
Included file
}
} -start
varnish v1 -vcl+backend {
import blobdigest from "${vmod_topbuild}/src/.libs/libvmod_blobdigest.so";
import blob;
sub vcl_init {
new a = blob.blob(IDENTITY, "a");
new dtop = blobdigest.digest(MD5, scope=TOP);
}
sub vcl_recv {
if (req.esi_level > 0) {
dtop.update(blob.decode(encoded=req.esi_level));
set req.url = req.url + req.esi_level;
}
}
sub vcl_backend_response {
set beresp.do_esi = true;
}
sub vcl_deliver {
dtop.update(a.get());
if (req.esi_level > 0) {
# this does not make it into the final response because
# esi level 0 deliver runs before level1 and level1
# response headers are not relevant
set resp.http.hash = blob.encode(HEX, blob=dtop.final());
}
}
} -start
logexpect l1 -v v1 -g request {
expect 0 1001 Begin "^req .* rxreq"
expect * = ReqAcct "^18 0 18 187 75 262$"
expect 0 = End
} -start
logexpect l2 -v v1 -g request {
expect * 1002 Begin "^bereq "
expect * = End
} -start
logexpect l3 -v v1 -g request {
expect * 1003 Begin "^req .* esi"
#echo -n 'a1a'|md5sum
expect * = RespHeader "^hash: 278571846829dfd761550039130ff75f$"
expect * = ReqAcct "^0 0 0 0 18 18$"
expect 0 = End
} -start
logexpect l4 -v v1 -g request {
expect * 1004 Begin "^bereq "
expect * = End
} -start
logexpect l5 -v v1 -g request {
expect * 1005 Begin "^req .* rxreq"
expect * = ReqAcct "^18 0 18 192 75 267$"
expect 0 = End
} -start
client c1 {
txreq
rxresp
expect resp.bodylen == 75
expect resp.status == 200
delay .1
# test that there is no difference on miss/hit
txreq
rxresp
expect resp.bodylen == 75
expect resp.status == 200
}
client c1 -run
varnish v1 -expect esi_errors == 0
logexpect l1 -wait
logexpect l2 -wait
logexpect l3 -wait
logexpect l4 -wait
logexpect l5 -wait
......@@ -55,6 +55,7 @@ client c1 {
} -run
# Test repeated calls of the digest.final() method
# and TOP scope error on the backend side
server s1 {
rxreq
txresp -hdr "Cache-Control: max-age=0"
......@@ -82,6 +83,13 @@ varnish v1 -vcl+backend {
}
new d2 = blobdigest.digest(MD5);
new dtop = blobdigest.digest(MD5, scope=TOP);
}
sub vcl_backend_fetch {
if (bereq.url == "/fail") {
dtop.update(blob.decode(encoded="illegal"));
}
}
sub vcl_backend_response {
......@@ -120,6 +128,17 @@ client c1 {
expect resp.http.d2_b2 == "F96B697D7CB7938D525A2F31AAF161D0"
expect resp.http.d2_c1 == "0CC175B9C0F1B6A831C399E269772661"
expect resp.http.d2_c2 == "0CC175B9C0F1B6A831C399E269772661"
txreq -url "/fail"
rxresp
expect resp.status == 503
} -run
logexpect l0 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect * * Begin "^bereq"
expect * = BereqURL "^/fail"
expect * = VCL_Error "^vmod blobdigest error: dtop.update..: object has TOP scope - only accessible in client VCL context$"
expect * = End
} -run
# digest.update() may not be called after digest.final()
......@@ -171,6 +190,12 @@ varnish v1 -vcl {
}
}
logexpect l1 -v v1 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error "^vmod blobdigest error: already finalized in d1.update..$"
expect * = End
} -start
client c1 {
txreq
rxresp
......@@ -178,11 +203,7 @@ client c1 {
expect resp.http.empty == "D41D8CD98F00B204E9800998ECF8427E"
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error "^vmod blobdigest error: already finalized in d1.update..$"
expect * = End
} -run
logexpect l1 -wait
# hash() is legal in vcl_init and vcl_fini (as of Varnish 5.0)
varnish v2 -arg "-p debug=+vclrel" -vcl {
......
......@@ -62,6 +62,12 @@ struct digest_task {
struct vmod_priv *result;
};
enum scope {
_SCOPE_INVALID = 0,
PRIV_TASK,
PRIV_TOP
};
struct vmod_blobdigest_digest {
unsigned magic;
#define VMOD_BLOBDIGEST_DIGEST_MAGIC 0xaccb2e25
......@@ -69,6 +75,7 @@ struct vmod_blobdigest_digest {
char *vcl_name;
struct vmod_priv *result;
enum algorithm hash;
enum scope scope;
};
struct vmod_blobdigest_hmac {
......@@ -237,21 +244,49 @@ WS_Contains(struct ws * const restrict ws, const void * const restrict ptr,
static struct digest_task *
get_scope(const struct vrt_ctx * const restrict ctx,
const struct vmod_blobdigest_digest * const restrict h)
const struct vmod_blobdigest_digest * const restrict h,
const char * const restrict method)
{
struct vmod_priv *priv;
struct digest_task *task;
struct ws *ws;
switch (h->scope) {
case PRIV_TASK:
priv = VRT_priv_task(ctx, (void *)h);
ws = ctx->ws;
break;
case PRIV_TOP: {
if (ctx->req == NULL) {
VERR(ctx, "%s.%s(): object has TOP scope - only "
"accessible in client VCL context",
h->vcl_name, method);
return NULL;
}
assert(ctx->req->top);
priv = VRT_priv_top(ctx, (void *)h);
ws = ctx->req->top->ws;
break;
}
default:
WRONG("invalid scope");
}
priv = VRT_priv_task(ctx, (void *)h);
AN(priv);
AN(ws);
if (priv->priv != NULL) {
CAST_OBJ(task, priv->priv, VMOD_BLOBDIGEST_DIGEST_TASK_MAGIC);
WS_Contains(ctx->ws, task, sizeof(struct digest_task));
WS_Contains(ws, task, sizeof(struct digest_task));
}
else {
if ((task = WS_Alloc(ctx->ws, sizeof(struct digest_task)))
== NULL)
task = WS_Alloc(ws, sizeof(struct digest_task));
if (task == NULL) {
VERRNOMEM(ctx, "allocating task data in %s.%s()",
h->vcl_name, method);
return NULL;
}
task->magic = VMOD_BLOBDIGEST_DIGEST_TASK_MAGIC;
memcpy(&task->ctx, &h->ctx, sizeof(hash_ctx));
task->result = NULL;
......@@ -262,12 +297,26 @@ get_scope(const struct vrt_ctx * const restrict ctx,
return task;
}
static inline enum scope
parse_scope(const char * const restrict p)
{
assert(p[0] == 'T');
if (p[1] == 'O') {
assert(p[2] == 'P');
return PRIV_TOP;
}
assert(p[1] == 'A' && p[2] == 'S' && p[3] == 'K');
return PRIV_TASK;
}
VCL_VOID
vmod_digest__init(VRT_CTX, struct vmod_blobdigest_digest **digestp,
const char *vcl_name, VCL_ENUM hashs, VCL_BLOB initb)
const char *vcl_name, VCL_ENUM hashs, VCL_BLOB initb,
VCL_ENUM scopes)
{
struct vmod_blobdigest_digest *digest;
enum algorithm hash = parse_algorithm(hashs);
enum scope scope = parse_scope(scopes);
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(digestp);
......@@ -277,6 +326,7 @@ vmod_digest__init(VRT_CTX, struct vmod_blobdigest_digest **digestp,
AN(digest);
*digestp = digest;
digest->scope = scope;
digest->hash = hash;
digest->vcl_name = strdup(vcl_name);
AN(digest->vcl_name);
......@@ -320,6 +370,10 @@ vmod_digest_update(VRT_CTX, struct vmod_blobdigest_digest *h, VCL_BLOB b)
return 0;
}
task = get_scope(ctx, h, "update");
if (task == NULL)
return 0;
if (b == NULL) {
VERR(ctx, "null BLOB passed to %s.update()", h->vcl_name);
return 0;
......@@ -331,12 +385,6 @@ vmod_digest_update(VRT_CTX, struct vmod_blobdigest_digest *h, VCL_BLOB b)
return 1;
}
task = get_scope(ctx, h);
if (task == NULL) {
VERRNOMEM(ctx, "allocating task data in %s.update()",
h->vcl_name);
return 0;
}
if (task->result != NULL) {
VERR(ctx, "already finalized in %s.update()", h->vcl_name);
return 0;
......@@ -375,12 +423,10 @@ vmod_digest_final(VRT_CTX, struct vmod_blobdigest_digest *h)
return b;
}
task = get_scope(ctx, h);
if (task == NULL) {
VERRNOMEM(ctx, "allocating task data in %s.final()",
h->vcl_name);
task = get_scope(ctx, h, "final");
if (task == NULL)
return NULL;
}
if (task->result != NULL)
return task->result;
......
......@@ -11,7 +11,7 @@ $Module blobdigest 3 digests, checksums and hmacs for the VCL blob type
::
new OBJECT = blobdigest.digest(ENUM hash [, BLOB init])
new OBJECT = blobdigest.digest(ENUM hash [, BLOB init], ["TASK"|"TOP"])
BOOL <obj>.update(BLOB)
BLOB <obj>.final()
......@@ -185,7 +185,8 @@ values:
* ``SHA3_512``
$Object digest(ENUM {CRC32, MD5, SHA1, SHA224, SHA256, SHA384, SHA512, SHA3_224,
SHA3_256, SHA3_384, SHA3_512} hash, BLOB init=0)
SHA3_256, SHA3_384, SHA3_512} hash,
BLOB init=0, ENUM {TASK, TOP} scope="TASK")
Initialize a message digest context for the algorithm ``hash``, and
optionally update it with ``init``. If ``init`` is left out, then an
......@@ -195,6 +196,13 @@ If an ``init`` BLOB is provided, then the message digests computed
from this object result with ``init`` prepended before any BLOBs added
by the ``.update()`` method.
By default, the scope of the message digest context is ``TASK``:
method calls affect separate context states for the client and backend
side.
``TOP`` scope provides a context across all ESI subrequests, but is
available only on the client side.
Example::
import blobdigest;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment