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"] ; ...@@ -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) BOOL <obj>.update(BLOB)
BLOB <obj>.final() BLOB <obj>.final()
...@@ -202,7 +202,7 @@ values: ...@@ -202,7 +202,7 @@ values:
CONTENTS 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) * 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) * 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) * BLOB hmacf(ENUM {MD5,SHA1,SHA224,SHA256,SHA384,SHA512,SHA3_224,SHA3_256,SHA3_384,SHA3_512}, BLOB, BLOB)
...@@ -215,7 +215,7 @@ digest ...@@ -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 Initialize a message digest context for the algorithm ``hash``, and
optionally update it with ``init``. If ``init`` is left out, then an 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 ...@@ -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 from this object result with ``init`` prepended before any BLOBs added
by the ``.update()`` method. 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:: Example::
import blobdigest; 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 { ...@@ -55,6 +55,7 @@ client c1 {
} -run } -run
# Test repeated calls of the digest.final() method # Test repeated calls of the digest.final() method
# and TOP scope error on the backend side
server s1 { server s1 {
rxreq rxreq
txresp -hdr "Cache-Control: max-age=0" txresp -hdr "Cache-Control: max-age=0"
...@@ -82,6 +83,13 @@ varnish v1 -vcl+backend { ...@@ -82,6 +83,13 @@ varnish v1 -vcl+backend {
} }
new d2 = blobdigest.digest(MD5); 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 { sub vcl_backend_response {
...@@ -120,6 +128,17 @@ client c1 { ...@@ -120,6 +128,17 @@ client c1 {
expect resp.http.d2_b2 == "F96B697D7CB7938D525A2F31AAF161D0" expect resp.http.d2_b2 == "F96B697D7CB7938D525A2F31AAF161D0"
expect resp.http.d2_c1 == "0CC175B9C0F1B6A831C399E269772661" expect resp.http.d2_c1 == "0CC175B9C0F1B6A831C399E269772661"
expect resp.http.d2_c2 == "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 } -run
# digest.update() may not be called after digest.final() # digest.update() may not be called after digest.final()
...@@ -171,6 +190,12 @@ varnish v1 -vcl { ...@@ -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 { client c1 {
txreq txreq
rxresp rxresp
...@@ -178,11 +203,7 @@ client c1 { ...@@ -178,11 +203,7 @@ client c1 {
expect resp.http.empty == "D41D8CD98F00B204E9800998ECF8427E" expect resp.http.empty == "D41D8CD98F00B204E9800998ECF8427E"
} -run } -run
logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" { logexpect l1 -wait
expect 0 * Begin req
expect * = VCL_Error "^vmod blobdigest error: already finalized in d1.update..$"
expect * = End
} -run
# hash() is legal in vcl_init and vcl_fini (as of Varnish 5.0) # hash() is legal in vcl_init and vcl_fini (as of Varnish 5.0)
varnish v2 -arg "-p debug=+vclrel" -vcl { varnish v2 -arg "-p debug=+vclrel" -vcl {
......
...@@ -62,6 +62,12 @@ struct digest_task { ...@@ -62,6 +62,12 @@ struct digest_task {
struct vmod_priv *result; struct vmod_priv *result;
}; };
enum scope {
_SCOPE_INVALID = 0,
PRIV_TASK,
PRIV_TOP
};
struct vmod_blobdigest_digest { struct vmod_blobdigest_digest {
unsigned magic; unsigned magic;
#define VMOD_BLOBDIGEST_DIGEST_MAGIC 0xaccb2e25 #define VMOD_BLOBDIGEST_DIGEST_MAGIC 0xaccb2e25
...@@ -69,6 +75,7 @@ struct vmod_blobdigest_digest { ...@@ -69,6 +75,7 @@ struct vmod_blobdigest_digest {
char *vcl_name; char *vcl_name;
struct vmod_priv *result; struct vmod_priv *result;
enum algorithm hash; enum algorithm hash;
enum scope scope;
}; };
struct vmod_blobdigest_hmac { struct vmod_blobdigest_hmac {
...@@ -237,21 +244,49 @@ WS_Contains(struct ws * const restrict ws, const void * const restrict ptr, ...@@ -237,21 +244,49 @@ WS_Contains(struct ws * const restrict ws, const void * const restrict ptr,
static struct digest_task * static struct digest_task *
get_scope(const struct vrt_ctx * const restrict ctx, 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 vmod_priv *priv;
struct digest_task *task; 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(priv);
AN(ws);
if (priv->priv != NULL) { if (priv->priv != NULL) {
CAST_OBJ(task, priv->priv, VMOD_BLOBDIGEST_DIGEST_TASK_MAGIC); 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 { else {
if ((task = WS_Alloc(ctx->ws, sizeof(struct digest_task))) task = WS_Alloc(ws, sizeof(struct digest_task));
== NULL) if (task == NULL) {
VERRNOMEM(ctx, "allocating task data in %s.%s()",
h->vcl_name, method);
return NULL; return NULL;
}
task->magic = VMOD_BLOBDIGEST_DIGEST_TASK_MAGIC; task->magic = VMOD_BLOBDIGEST_DIGEST_TASK_MAGIC;
memcpy(&task->ctx, &h->ctx, sizeof(hash_ctx)); memcpy(&task->ctx, &h->ctx, sizeof(hash_ctx));
task->result = NULL; task->result = NULL;
...@@ -262,12 +297,26 @@ get_scope(const struct vrt_ctx * const restrict ctx, ...@@ -262,12 +297,26 @@ get_scope(const struct vrt_ctx * const restrict ctx,
return task; 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 VCL_VOID
vmod_digest__init(VRT_CTX, struct vmod_blobdigest_digest **digestp, 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; struct vmod_blobdigest_digest *digest;
enum algorithm hash = parse_algorithm(hashs); enum algorithm hash = parse_algorithm(hashs);
enum scope scope = parse_scope(scopes);
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(digestp); AN(digestp);
...@@ -277,6 +326,7 @@ vmod_digest__init(VRT_CTX, struct vmod_blobdigest_digest **digestp, ...@@ -277,6 +326,7 @@ vmod_digest__init(VRT_CTX, struct vmod_blobdigest_digest **digestp,
AN(digest); AN(digest);
*digestp = digest; *digestp = digest;
digest->scope = scope;
digest->hash = hash; digest->hash = hash;
digest->vcl_name = strdup(vcl_name); digest->vcl_name = strdup(vcl_name);
AN(digest->vcl_name); AN(digest->vcl_name);
...@@ -320,6 +370,10 @@ vmod_digest_update(VRT_CTX, struct vmod_blobdigest_digest *h, VCL_BLOB b) ...@@ -320,6 +370,10 @@ vmod_digest_update(VRT_CTX, struct vmod_blobdigest_digest *h, VCL_BLOB b)
return 0; return 0;
} }
task = get_scope(ctx, h, "update");
if (task == NULL)
return 0;
if (b == NULL) { if (b == NULL) {
VERR(ctx, "null BLOB passed to %s.update()", h->vcl_name); VERR(ctx, "null BLOB passed to %s.update()", h->vcl_name);
return 0; return 0;
...@@ -331,12 +385,6 @@ vmod_digest_update(VRT_CTX, struct vmod_blobdigest_digest *h, VCL_BLOB b) ...@@ -331,12 +385,6 @@ vmod_digest_update(VRT_CTX, struct vmod_blobdigest_digest *h, VCL_BLOB b)
return 1; 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) { if (task->result != NULL) {
VERR(ctx, "already finalized in %s.update()", h->vcl_name); VERR(ctx, "already finalized in %s.update()", h->vcl_name);
return 0; return 0;
...@@ -375,12 +423,10 @@ vmod_digest_final(VRT_CTX, struct vmod_blobdigest_digest *h) ...@@ -375,12 +423,10 @@ vmod_digest_final(VRT_CTX, struct vmod_blobdigest_digest *h)
return b; return b;
} }
task = get_scope(ctx, h); task = get_scope(ctx, h, "final");
if (task == NULL) { if (task == NULL)
VERRNOMEM(ctx, "allocating task data in %s.final()",
h->vcl_name);
return NULL; return NULL;
}
if (task->result != NULL) if (task->result != NULL)
return task->result; return task->result;
......
...@@ -11,7 +11,7 @@ $Module blobdigest 3 digests, checksums and hmacs for the VCL blob type ...@@ -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) BOOL <obj>.update(BLOB)
BLOB <obj>.final() BLOB <obj>.final()
...@@ -185,7 +185,8 @@ values: ...@@ -185,7 +185,8 @@ values:
* ``SHA3_512`` * ``SHA3_512``
$Object digest(ENUM {CRC32, MD5, SHA1, SHA224, SHA256, SHA384, SHA512, SHA3_224, $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 Initialize a message digest context for the algorithm ``hash``, and
optionally update it with ``init``. If ``init`` is left out, then an 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 ...@@ -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 from this object result with ``init`` prepended before any BLOBs added
by the ``.update()`` method. 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:: Example::
import blobdigest; 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