[5.2] ff3ab53 VMOD blob: use a DEFAULT value for the case ENUM.

PÃ¥l Hermunn Johansen hermunn at varnish-software.com
Fri Sep 15 11:17:31 UTC 2017


commit ff3ab5340c62d354c518592601a26dc2f250d0c7
Author: Geoff Simmons <geoff at uplex.de>
Date:   Thu Sep 14 17:42:23 2017 +0200

    VMOD blob: use a DEFAULT value for the case ENUM.
    
    DEFAULT is interpreted as LOWER for the HEX and URL encodings, and
    is the only legal value for all other encodings.

diff --git a/bin/varnishtest/tests/m00033.vtc b/bin/varnishtest/tests/m00033.vtc
index e4d963c..02d896d 100644
--- a/bin/varnishtest/tests/m00033.vtc
+++ b/bin/varnishtest/tests/m00033.vtc
@@ -54,13 +54,9 @@ varnish v1 -vcl {
 	      blob.encode(IDENTITY,
 			  blob=blob.decode(HEX, encoded="666f6f00626172"));
 
-	    set resp.http.lc =
-	      blob.encode(IDENTITY, LOWER, blob.decode(IDENTITY,
-						       encoded="Don't care"));
-
-	    set resp.http.uc =
-	      blob.encode(IDENTITY, UPPER, blob.decode(IDENTITY,
-						       encoded="Don't care"));
+	    set resp.http.pos =
+	      blob.encode(IDENTITY, DEFAULT, blob.decode(IDENTITY,
+						         encoded="foobar"));
 	}
 } -start
 
@@ -76,6 +72,52 @@ client c1 {
 	expect resp.http.param == "The quick brown fox jumps over the lazy dog"
 	expect resp.http.paramlist == "/The quick brown fox jumps over the lazy dog/"
 	expect resp.http.truncated == "foo"
-	expect resp.http.lc == "Don't care"
-	expect resp.http.uc == "Don't care"
+	expect resp.http.pos == "foobar"
 } -run
+
+# Require case=DEFAULT
+
+server s1 -repeat 2 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl+backend {
+	import blob;
+
+	sub vcl_deliver {
+	  if (req.url == "/lc") {
+	    set resp.http.lc =
+	      blob.encode(IDENTITY, LOWER, blob.decode(IDENTITY,
+						       encoded="foobar"));
+	  }
+	  elsif (req.url == "/uc") {
+	    set resp.http.uc =
+	      blob.encode(IDENTITY, UPPER, blob.decode(IDENTITY,
+						       encoded="foobar"));
+	  }
+	}
+}
+
+client c1 {
+	txreq -url "/lc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.lc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/uc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.uc == <undef>
+} -run
+
+logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
+	expect * * VCL_Error "^vmod blob error: case LOWER is illegal with encoding IDENTITY$"
+	expect * * VCL_Error "^vmod blob error: case UPPER is illegal with encoding IDENTITY$"
+} -start
+
+logexpect l1 -wait
diff --git a/bin/varnishtest/tests/m00037.vtc b/bin/varnishtest/tests/m00037.vtc
index 3209394..e545f1c 100644
--- a/bin/varnishtest/tests/m00037.vtc
+++ b/bin/varnishtest/tests/m00037.vtc
@@ -21,13 +21,9 @@ varnish v1 -vcl {
 	      blob.encode(BASE64, blob=blob.decode(IDENTITY, encoded=
 						      req.http.pangram));
 
-	    set resp.http.b64lc =
-	      blob.encode(BASE64, LOWER, blob.decode(IDENTITY, encoded=
-						      req.http.pangram));
-
-	    set resp.http.b64uc =
-	      blob.encode(BASE64, UPPER, blob.decode(IDENTITY, encoded=
-						      req.http.pangram));
+	    set resp.http.b64pos =
+	      blob.encode(BASE64, DEFAULT, blob.decode(IDENTITY, encoded=
+						       req.http.pangram));
 
 	    set resp.http.b64hobbes =
 	      blob.encode(BASE64, blob=blob.decode(IDENTITY, encoded=
@@ -41,12 +37,8 @@ varnish v1 -vcl {
 	      blob.encode(BASE64URL,
 			  blob=blob.decode(IDENTITY, encoded=req.http.pangram));
 
-	    set resp.http.b64urllc =
-	      blob.encode(BASE64URL, LOWER,
-			  blob.decode(IDENTITY, encoded=req.http.pangram));
-
-	    set resp.http.b64urluc =
-	      blob.encode(BASE64URL, UPPER,
+	    set resp.http.b64urlpos =
+	      blob.encode(BASE64URL, DEFAULT,
 			  blob.decode(IDENTITY, encoded=req.http.pangram));
 
 	    set resp.http.b64urlhobbes =
@@ -61,12 +53,8 @@ varnish v1 -vcl {
 	      blob.encode(BASE64URLNOPAD,
 			  blob=blob.decode(IDENTITY, encoded=req.http.pangram));
 
-	    set resp.http.b64urlnopadlc =
-	      blob.encode(BASE64URLNOPAD, LOWER,
-			  blob.decode(IDENTITY, encoded=req.http.pangram));
-
-	    set resp.http.b64urlnopaduc =
-	      blob.encode(BASE64URLNOPAD, UPPER,
+	    set resp.http.b64urlnopadpos =
+	      blob.encode(BASE64URLNOPAD, DEFAULT,
 			  blob.decode(IDENTITY, encoded=req.http.pangram));
 
 	    set resp.http.b64nopadhobbes =
@@ -87,15 +75,15 @@ varnish v1 -vcl {
 
 	    set resp.http.b64param =
 	      blob.encode(blob=blob.decode(IDENTITY, encoded=req.http.pangram),
-			  encoding=BASE64, case=LOWER);
+			  encoding=BASE64, case=DEFAULT);
 
 	    set resp.http.b64urlparam =
 	      blob.encode(blob=blob.decode(IDENTITY, encoded=req.http.pangram),
-			  encoding=BASE64URL, case=UPPER);
+			  encoding=BASE64URL, case=DEFAULT);
 
 	    set resp.http.b64urlnopadparam =
 	      blob.encode(blob=blob.decode(IDENTITY, encoded=req.http.pangram),
-			  encoding=BASE64URLNOPAD, case=LOWER);
+			  encoding=BASE64URLNOPAD, case=DEFAULT);
 
 	    set resp.http.b64xcode =
 		blob.transcode(IDENTITY, BASE64,
@@ -115,18 +103,15 @@ client c1 {
 	txreq -url "/"
 	rxresp
 	expect resp.http.b64 == "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw=="
-	expect resp.http.b64lc == resp.http.b64
-	expect resp.http.b64uc == resp.http.b64
+	expect resp.http.b64pos == resp.http.b64
 	expect resp.http.b64hobbes == "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="
 	expect resp.http.b64all == "//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAA=="
 	expect resp.http.b64url == "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw=="
-	expect resp.http.b64urllc == resp.http.b64url
-	expect resp.http.b64urluc == resp.http.b64url
+	expect resp.http.b64urlpos == resp.http.b64url
 	expect resp.http.b64urlhobbes == "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="
 	expect resp.http.b64urlall == "__79_Pv6-fj39vX08_Lx8O_u7ezr6uno5-bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL--vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI-OjYyLiomIh4aFhIOCgYB_fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAA=="
 	expect resp.http.b64urlnopad == "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw"
-	expect resp.http.b64urlnopadlc == resp.http.b64urlnopad
-	expect resp.http.b64urlnopaduc == resp.http.b64urlnopad
+	expect resp.http.b64urlnopadpos == resp.http.b64urlnopad
 	expect resp.http.b64nopadhobbes == "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4"
 	expect resp.http.b64nopadall == "__79_Pv6-fj39vX08_Lx8O_u7ezr6uno5-bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL--vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI-OjYyLiomIh4aFhIOCgYB_fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAA"
 	expect resp.http.b64empty == ""
@@ -339,3 +324,102 @@ logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
 	expect * * VCL_Error {^vmod blob error: cannot decode, illegal encoding beginning with "././"$}
 	expect * * VCL_Error {^vmod blob error: cannot decode, illegal encoding beginning with "TWFu"$}
 } -run
+
+# Require case=DEFAULT
+
+server s1 -repeat 6 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl+backend {
+	import blob;
+
+	sub vcl_deliver {
+	  if (req.url == "/b64lc") {
+	    set resp.http.b64lc =
+	      blob.encode(BASE64, LOWER, blob.decode(IDENTITY, encoded=""));
+	  }
+	  elsif (req.url == "/b64uc") {
+	    set resp.http.b64uc =
+	      blob.encode(BASE64, UPPER, blob.decode(IDENTITY, encoded=""));
+	  }
+	  elsif (req.url == "/b64urllc") {
+	    set resp.http.b64urllc =
+	      blob.encode(BASE64URL, LOWER, blob.decode(IDENTITY, encoded=""));
+	  }
+	  elsif (req.url == "/b64urluc") {
+	    set resp.http.b64urluc =
+	      blob.encode(BASE64URL, UPPER, blob.decode(IDENTITY, encoded=""));
+	  }
+	  elsif (req.url == "/b64urlnopadlc") {
+	    set resp.http.b64urlnopadlc =
+	      blob.encode(BASE64URLNOPAD, LOWER, blob.decode(IDENTITY,
+							     encoded=""));
+	  }
+	  elsif (req.url == "/b64urlnopaduc") {
+	    set resp.http.b64urlnopaduc =
+	      blob.encode(BASE64URLNOPAD, UPPER, blob.decode(IDENTITY,
+							     encoded=""));
+	  }
+	}
+}
+
+client c1 {
+	txreq -url "/b64lc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.b64lc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/b64uc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.b64uc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/b64urllc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.b64urllc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/b64urluc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.b64urluc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/b64urlnopadlc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.b64urlnopadlc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/b64urlnopaduc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.b64urlnopaduc == <undef>
+} -run
+
+logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
+	expect * * VCL_Error "^vmod blob error: case LOWER is illegal with encoding BASE64"
+	expect * * VCL_Error "^vmod blob error: case UPPER is illegal with encoding BASE64"
+	expect * * VCL_Error "^vmod blob error: case LOWER is illegal with encoding BASE64URL"
+	expect * * VCL_Error "^vmod blob error: case UPPER is illegal with encoding BASE64URL"
+	expect * * VCL_Error "^vmod blob error: case LOWER is illegal with encoding BASE64URLNOPAD"
+	expect * * VCL_Error "^vmod blob error: case UPPER is illegal with encoding BASE64URLNOPAD"
+} -start
+
+logexpect l1 -wait
diff --git a/bin/varnishtest/tests/m00043.vtc b/bin/varnishtest/tests/m00043.vtc
index 7521255..9062314 100644
--- a/bin/varnishtest/tests/m00043.vtc
+++ b/bin/varnishtest/tests/m00043.vtc
@@ -1,9 +1,8 @@
 varnishtest "VMOD blob blob object interface"
 
-server s1 {} -start
-
-varnish v1 -arg "-i serverid" -vcl+backend {
+varnish v1 -arg "-i serverid" -vcl {
 	import blob;
+	backend b { .host = "${bad_ip}"; }
 
 	sub vcl_init {
 	  new id = blob.blob(IDENTITY,
@@ -93,8 +92,9 @@ client c1 {
 # run twice to test retrieving cached encodings
 client c1 -run
 
-varnish v1 -vcl+backend {
+varnish v1 -vcl {
 	import blob;
+	backend b { .host = "${bad_ip}"; }
 
 	sub vcl_init {
 	  new idempty = blob.blob(IDENTITY, "");
@@ -165,8 +165,9 @@ client c1 {
 # run twice to test retrieving cached encodings
 client c1 -run
 
-varnish v1 -vcl+backend {
+varnish v1 -vcl {
 	import blob;
+	backend b { .host = "${bad_ip}"; }
 
 	sub vcl_init {
 	  new b64 = blob.blob(BASE64, "L0hlbGxvIHdvcmxkLw==");
@@ -254,8 +255,9 @@ client c2 {
 # run twice
 client c2 -run
 
-varnish v1 -vcl+backend {
+varnish v1 -vcl {
 	import blob;
+	backend b { .host = "${bad_ip}"; }
 
 	sub vcl_init {
 	  new id = blob.blob(IDENTITY,
@@ -332,6 +334,125 @@ client c3 -run
 varnish v1 -cliok "vcl.discard vcl1"
 varnish v1 -cliok "vcl.discard vcl2"
 
+# Require case=DEFAULT in the .encode() method where necessary
+
+server s1 -repeat 8 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl+backend {
+	import blob;
+
+	sub vcl_init {
+	  new b = blob.blob(IDENTITY, "");
+	}
+
+	sub vcl_deliver {
+	  if (req.url == "/idlc") {
+	    set resp.http.idlc = b.encode(IDENTITY, LOWER);
+	  }
+	  elsif (req.url == "/iduc") {
+	    set resp.http.iduc = b.encode(IDENTITY, UPPER);
+	  }
+	  if (req.url == "/b64lc") {
+	    set resp.http.b64lc = b.encode(BASE64, LOWER);
+	  }
+	  elsif (req.url == "/b64uc") {
+	    set resp.http.b64uc = b.encode(BASE64, UPPER);
+	  }
+	  elsif (req.url == "/b64urllc") {
+	    set resp.http.b64urllc = b.encode(BASE64URL, LOWER);
+	  }
+	  elsif (req.url == "/b64urluc") {
+	    set resp.http.b64urluc = b.encode(BASE64URL, UPPER);
+	  }
+	  elsif (req.url == "/b64urlnopadlc") {
+	    set resp.http.b64urlnopadlc = b.encode(BASE64URLNOPAD, LOWER);
+	  }
+	  elsif (req.url == "/b64urlnopaduc") {
+	    set resp.http.b64urlnopaduc = b.encode(BASE64URLNOPAD, UPPER);
+	  }
+	}
+}
+
+client c1 {
+	txreq -url "/idlc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.idlc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/iduc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.iduc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/b64lc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.b64lc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/b64uc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.b64uc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/b64urllc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.b64urllc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/b64urluc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.b64urluc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/b64urlnopadlc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.b64urlnopadlc == <undef>
+} -run
+
+client c1 {
+	txreq -url "/b64urlnopaduc"
+	rxresp
+	expect resp.status == 503
+	expect resp.reason == "VCL failed"
+	expect resp.http.b64urlnopaduc == <undef>
+} -run
+
+logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
+	expect * * VCL_Error "^vmod blob error: case LOWER is illegal with encoding IDENTITY$"
+	expect * * VCL_Error "^vmod blob error: case UPPER is illegal with encoding IDENTITY$"
+	expect * * VCL_Error "^vmod blob error: case LOWER is illegal with encoding BASE64$"
+	expect * * VCL_Error "^vmod blob error: case UPPER is illegal with encoding BASE64$"
+	expect * * VCL_Error "^vmod blob error: case LOWER is illegal with encoding BASE64URL$"
+	expect * * VCL_Error "^vmod blob error: case UPPER is illegal with encoding BASE64URL$"
+	expect * * VCL_Error "^vmod blob error: case LOWER is illegal with encoding BASE64URLNOPAD$"
+	expect * * VCL_Error "^vmod blob error: case UPPER is illegal with encoding BASE64URLNOPAD$"
+} -start
+
+logexpect l1 -wait
+
 varnish v1 -errvcl {vmod blob error: cannot create blob err, illegal encoding beginning with "g"} {
 	import blob;
 	backend b { .host="${bad_ip}"; }
diff --git a/lib/libvmod_blob/vmod.vcc b/lib/libvmod_blob/vmod.vcc
index 96dea4e..3b90d3a 100644
--- a/lib/libvmod_blob/vmod.vcc
+++ b/lib/libvmod_blob/vmod.vcc
@@ -94,16 +94,19 @@ Empty strings are decoded into a "null blob" (of length 0),
 and conversely a null blob is encoded as the empty string.
 
 For encodings with ``HEX`` or ``URL``, you may also specify a ``case``
-ENUM with one of the values ``LOWER`` or ``UPPER`` to produce a string
-with lower- or uppercase hex digits (in ``[a-f]`` or ``[A-F]``),
-respectively. The default value for ``case`` is ``LOWER``.
+ENUM with one of the values ``LOWER``, ``UPPER`` or ``DEFAULT`` to
+produce a string with lower- or uppercase hex digits (in ``[a-f]`` or
+``[A-F]``). The default value for ``case`` is ``DEFAULT``, which for
+``HEX`` and ``URL`` means the same as ``LOWER``.
 
 The ``case`` ENUM is not relevant for decodings; ``HEX`` or ``URL``
 strings to be decoded as BLOBs may have hex digits in either case, or
-in mixed case. The ``case`` ENUM is also ignored for all other
-encodings.  You cannot, for example, produce an uppercase string by
-using the IDENTITY scheme with ``case=UPPER``. To change the case of a
-string, use the ``toupper`` or ``tolower`` functions from
+in mixed case.
+
+The ``case`` ENUM MUST be set to ``DEFAULT`` for the other encodings
+(BASE64* and IDENTITY).  You cannot, for example, produce an uppercase
+string by using the IDENTITY scheme with ``case=UPPER``. To change the
+case of a string, use the ``toupper`` or ``tolower`` functions from
 :ref:`vmod_std(3)`.
 
 IDENTITY
@@ -133,7 +136,7 @@ be written as::
   set resp.http.Trunced-Foo2
     = blob.encode(blob=blob.decode(HEX, encoded="666f6f00626172"));
 
-The ``case`` ENUM is ignored for ``IDENTITY`` encodings.
+The ``case`` ENUM MUST be set to ``DEFAULT`` for ``IDENTITY`` encodings.
 
 BASE64*
 -------
@@ -155,16 +158,17 @@ The ``BASE64URLNOPAD`` encoding uses the same alphabet as
 ``BASE6URL``, but leaves out the padding. Thus the length of an
 encoding with this scheme is not necessarily a mutltiple of four.
 
-The ``case`` ENUM is ignored for for all of the ``BASE64*`` encodings.
+The ``case`` ENUM MUST be set to ``DEFAULT`` for for all of the
+``BASE64*`` encodings.
 
 HEX
 ---
 
 The ``HEX`` encoding scheme converts hex strings into blobs and vice
 versa. For encodings, you may use the ``case`` ENUM to specify upper-
-or lowercase hex digits ``A`` through ``f`` (default ``LOWER``).  A
-prefix such as ``0x`` is not used for an encoding and is illegal for a
-decoding.
+or lowercase hex digits ``A`` through ``f`` (default ``DEFAULT``,
+which means the same as ``LOWER``).  A prefix such as ``0x`` is not
+used for an encoding and is illegal for a decoding.
 
 If a hex string to be decoded has an odd number of digits, it is
 decoded as if a ``0`` is prepended to it; that is, the first digit is
@@ -214,13 +218,15 @@ Example::
 
 $Function STRING encode(ENUM {IDENTITY, BASE64, BASE64URL, BASE64URLNOPAD,
 			      HEX, URL} encoding="IDENTITY",
-			ENUM {LOWER, UPPER} case="LOWER", BLOB blob)
+			ENUM {LOWER, UPPER, DEFAULT} case="DEFAULT", BLOB blob)
 
 Returns a string representation of the BLOB ``blob`` as specifed by
 ``encoding``. ``case`` determines the case of hex digits for the
 ``HEX`` and ``URL`` encodings, and is ignored for the other encodings.
 
-``encoding`` defaults to IDENTITY, and ``case`` defaults to LOWER.
+``encoding`` defaults to IDENTITY, and ``case`` defaults to DEFAULT.
+DEFAULT is interpreted as LOWER for the HEX and URL encodings, and is
+the required value for the other encodings.
 
 Example::
 
@@ -242,8 +248,8 @@ $Function STRING transcode(ENUM {IDENTITY, BASE64, BASE64URL, BASE64URLNOPAD,
 				 HEX, URL} decoding="IDENTITY",
 			   ENUM {IDENTITY, BASE64, BASE64URL, BASE64URLNOPAD,
 				 HEX, URL} encoding="IDENTITY",
-			   ENUM {LOWER, UPPER} case="LOWER", INT length=0,
-			   STRING_LIST encoded)
+			   ENUM {LOWER, UPPER, DEFAULT} case="DEFAULT",
+			   INT length=0, STRING_LIST encoded)
 
 Translates from one encoding to another, by first decoding the string
 ``encoded`` according to the scheme ``decoding``, and then returning
@@ -256,7 +262,8 @@ As with ``decode()``: If ``length`` > 0, only decode the first
 entire string. The default value of ``length`` is 0.
 
 ``decoding`` and ``encoding`` default to IDENTITY, and ``case``
-defaults to LOWER.
+defaults to DEFAULT. DEFAULT is interpreted as LOWER for the HEX and
+URL encodings, and is the required value for the other encodings.
 
 Example::
 
@@ -343,12 +350,12 @@ Example::
 
 $Method STRING .encode(ENUM {IDENTITY, BASE64, BASE64URL, BASE64URLNOPAD, HEX,
 			     URL} encoding="IDENTITY",
-		       ENUM {LOWER, UPPER} case="LOWER")
+		       ENUM {LOWER, UPPER, DEFAULT} case="DEFAULT")
 
 Returns an encoding of BLOB created by the constructor, according to
 the scheme ``encoding``. ``case`` determines the case of hex digits
-for the ``HEX`` and ``URL`` encodings, and is ignored for other
-encodings.
+for the ``HEX`` and ``URL`` encodings, and MUST be set to ``DEFAULT``
+for the other encodings.
 
 Example::
 
@@ -378,10 +385,11 @@ not known until runtime.
 ERRORS
 ======
 
-The encoders, decoders and ``sub()`` may fail if there is
-insufficient space to create the new blob or string. Decoders may also
-fail if the encoded string is an illegal format for the decoding
-scheme.
+The encoders, decoders and ``sub()`` may fail if there is insufficient
+space to create the new blob or string. Decoders may also fail if the
+encoded string is an illegal format for the decoding scheme. Encoders
+will fail for the ``IDENTITY`` and ``BASE64*`` encoding schemes if the
+``case`` ENUM is not set to ``DEFAULT``.
 
 If any of the VMOD's methods, functions or constructor fail, then VCL
 failure is invoked, just as if ``return(fail)`` had been called in the
diff --git a/lib/libvmod_blob/vmod_blob.c b/lib/libvmod_blob/vmod_blob.c
index 7ded3a8..7e58db1 100644
--- a/lib/libvmod_blob/vmod_blob.c
+++ b/lib/libvmod_blob/vmod_blob.c
@@ -159,6 +159,9 @@ static inline enum case_e
 parse_case(VCL_ENUM case_s)
 {
 	switch(*case_s) {
+	case 'D':
+		AZ(strcmp(case_s + 1, "EFAULT"));
+		return DEFAULT;
 	case 'L':
 		AZ(strcmp(case_s + 1, "OWER"));
 		return LOWER;
@@ -170,6 +173,25 @@ parse_case(VCL_ENUM case_s)
 	}
 }
 
+static inline int
+encodes_hex(enum encoding enc)
+{
+	return (enc == HEX || enc == URL);
+}
+
+/* Require case DEFAULT for all encodings besides HEX and URL. */
+
+static inline int
+check_enc_case(VRT_CTX, VCL_ENUM encs, VCL_ENUM case_s, enum encoding enc,
+	       enum case_e kase)
+{
+	if (!encodes_hex(enc) && kase != DEFAULT) {
+		VERR(ctx, "case %s is illegal with encoding %s", case_s, encs);
+		return 0;
+	}
+	return 1;
+}
+
 /* Objects */
 
 VCL_VOID __match_proto__(td_blob_blob__init)
@@ -250,8 +272,12 @@ vmod_blob_encode(VRT_CTX, struct vmod_blob_blob *b, VCL_ENUM encs,
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
 	CHECK_OBJ_NOTNULL(b, VMOD_BLOB_MAGIC);
 
+	if (!check_enc_case(ctx, encs, case_s, enc, kase))
+		return NULL;
 	if (b->blob.len == 0)
 		return "";
+	if (kase == DEFAULT)
+		kase = LOWER;
 
 	if (b->encoding[enc][kase] == NULL) {
 		AZ(pthread_mutex_lock(&b->lock));
@@ -396,7 +422,6 @@ encode(VRT_CTX, enum encoding enc, enum case_e kase, VCL_BLOB b)
 	struct wb_s wb;
 	ssize_t len;
 
-	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
 	AENC(enc);
 
 	if (b == NULL)
@@ -429,13 +454,11 @@ vmod_encode(VRT_CTX, VCL_ENUM encs, VCL_ENUM case_s, VCL_BLOB b)
 {
 	enum encoding enc = parse_encoding(encs);
 	enum case_e kase = parse_case(case_s);
-	return encode(ctx, enc, kase, b);
-}
 
-static inline int
-encodes_hex(enum encoding enc)
-{
-	return (enc == HEX || enc == URL);
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	if (!check_enc_case(ctx, encs, case_s, enc, kase))
+		return NULL;
+	return encode(ctx, enc, kase, b);
 }
 
 VCL_STRING __match_proto__(td_blob_transcode)
@@ -455,6 +478,9 @@ vmod_transcode(VRT_CTX, VCL_ENUM decs, VCL_ENUM encs, VCL_ENUM case_s,
 	AENC(dec);
 	AENC(enc);
 
+	if (!check_enc_case(ctx, encs, case_s, enc, kase))
+		return NULL;
+
 	/*
 	 * Allocate space for the decoded blob on the stack
 	 * ignoring the limitation imposed by n
diff --git a/lib/libvmod_blob/vmod_blob.h b/lib/libvmod_blob/vmod_blob.h
index 9cc1d45..04663e5 100644
--- a/lib/libvmod_blob/vmod_blob.h
+++ b/lib/libvmod_blob/vmod_blob.h
@@ -34,9 +34,14 @@
 
 #define AENC(enc) assert((enc) > _INVALID && (enc) < __MAX_ENCODING)
 
+/*
+ * The enums MUST appear in this order, since LOWER and UPPER are used to
+ * index the array of cached encodings for the blob object.
+ */
 enum case_e {
 	LOWER,
 	UPPER,
+	DEFAULT,
 };
 
 /*


More information about the varnish-commit mailing list