[master] 5a1a9b089 make vmod_std conversions more flexible

Nils Goroll nils.goroll at uplex.de
Mon Mar 4 13:01:08 UTC 2019


commit 5a1a9b0894667b9b1bcf8671b26690ee9a78001f
Author: Nils Goroll <nils.goroll at uplex.de>
Date:   Wed Feb 27 18:42:28 2019 +0100

    make vmod_std conversions more flexible
    
    In order to avoid the cross product problem with conversion functions
    (from x to y types would require x*y functions), we add flexibility
    regarding the input parameters to conversion functions:
    
    Each convertion function named after the destination type now takes all
    sensible arguments by name.
    
    int is named integer because of the reserved symbol name "int" in C.
    
    All functions should be fully backwards compatible (existing vtcs
    continue to work), but compile time checks are now effectively removed.
    The conversion functions now trigger vcl errors if used incorrectly or
    if conversion errors occur and no failback is provided. However, if a
    failback is provided, vcl errors are only raised for usage errors.
    
    For consistency, the conversion functions now only ever truncate if
    necessary. std.round() is added for explicit rounding where required.
    
    Existing functions which are now obsolete are marked deprecated.
    
    Ref #2899
    Ref https://github.com/varnishcache/varnish-cache/wiki/VIP12:-vmod-polymorphism-(for-type-conversions)

diff --git a/bin/varnishtest/tests/m00005.vtc b/bin/varnishtest/tests/m00005.vtc
index 9ce01bf42..253f49584 100644
--- a/bin/varnishtest/tests/m00005.vtc
+++ b/bin/varnishtest/tests/m00005.vtc
@@ -9,8 +9,36 @@ varnish v1 -vcl+backend {
 	import std;
 
 	sub vcl_deliver {
-		set resp.http.duration = std.duration(req.http.duration, 1s) + 1000000s;
-		set resp.http.bytes = std.bytes(req.http.bytes, 10K) + 512B;
+		if (req.http.d-real) {
+			set resp.http.duration =
+			    std.duration(real=std.real(req.http.d-real))
+			    + 1000000s;
+		} else if (req.http.d-int) {
+			set resp.http.duration =
+			    std.duration(integer=std.integer(req.http.d-int))
+			    + 1000000s;
+		} else if (req.http.duration-nf) {
+			set resp.http.duration =
+			    std.duration(req.http.duration) + 1000000s;
+		} else {
+			set resp.http.duration =
+			    std.duration(req.http.duration, 1s) + 1000000s;
+		}
+		if (req.http.b-real) {
+			set resp.http.bytes =
+			    std.bytes(real=std.real(req.http.b-real))
+			    + 512B;
+		} else if (req.http.b-int) {
+			set resp.http.bytes =
+			    std.bytes(integer=std.integer(req.http.b-int))
+			    + 512B;
+		} else if (req.http.bytes-nf) {
+			set resp.http.bytes = std.bytes(req.http.bytes-nf)
+			    + 512B;
+		} else {
+			set resp.http.bytes = std.bytes(req.http.bytes, 10K)
+			    + 512B;
+		}
 	}
 } -start
 
@@ -82,4 +110,100 @@ client c1 {
 	txreq -hdr "duration: 100"
 	rxresp
 	expect resp.http.duration == 1000001.000
+
+	# ---
+
+	txreq -hdr "d-real: 0.010" -hdr "b-real: 2048.99999"
+	rxresp
+	expect resp.http.duration == 1000000.010
+	expect resp.http.bytes == 2560.000
+
+	txreq -hdr "d-real: 10.1" -hdr "b-real: 3145728.9"
+	rxresp
+	expect resp.http.duration == 1000010.100
+	expect resp.http.bytes == 3146240.000
+
+	txreq -hdr "d-real: 600" -hdr "b-real: 4831838208.123"
+	rxresp
+	expect resp.http.duration == 1000600.000
+	expect resp.http.bytes == 4831838720.000
+
+	txreq -hdr "d-real: 36000" -hdr "b-real: 131941395333.12"
+	rxresp
+	expect resp.http.duration == 1036000.000
+	expect resp.http.bytes == 131941395845.000
+
+	txreq -hdr "d-real: -100"
+	rxresp
+	expect resp.http.duration ==  999900.000
+
+	txreq -hdr "d-real: -inf"
+	rxresp
+	expect resp.status == 503
+} -run
+
+client c1 {
+	txreq -hdr "d-int: 600" -hdr "b-int: 4831838208"
+	rxresp
+	expect resp.http.duration == 1000600.000
+	expect resp.http.bytes == 4831838720.000
+
+	txreq -hdr "d-int: 36000" -hdr "b-int: 131941395333"
+	rxresp
+	expect resp.http.duration == 1036000.000
+	expect resp.http.bytes == 131941395845.000
+
+	txreq -hdr "d-int: -100"
+	rxresp
+	expect resp.http.duration ==  999900.000
+
+	txreq -hdr "d-int: x"
+	rxresp
+	expect resp.status == 503
+} -run
+
+client c1 {
+	txreq -hdr "duration-nf: s"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "duration-nf: 3wx"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "duration-nf: -inf"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "duration-nf: 2x"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "duration-nf: 2h x"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "duration-nf: 100"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "bytes-nf: x"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "b-real: x"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "b-int: x"
+	rxresp
+	expect resp.status == 503
 } -run
diff --git a/bin/varnishtest/tests/m00007.vtc b/bin/varnishtest/tests/m00007.vtc
index b66fc0b98..51840f5dd 100644
--- a/bin/varnishtest/tests/m00007.vtc
+++ b/bin/varnishtest/tests/m00007.vtc
@@ -13,16 +13,57 @@ varnish v1 -vcl+backend {
 		set resp.http.ltzero = (std.integer(req.http.foo, 0) < 0);
 		set resp.http.iszero = (std.integer(req.http.foo, 0) == 0);
 		set resp.http.converted = std.integer(req.http.foo, 0);
+
+		if (req.http.nofb) {
+			set resp.http.nofb = std.integer(req.http.foo);
+		}
+
+		if (req.http.bool) {
+			set resp.http.bool =
+			    std.integer(bool=req.http.bool != "");
+		}
+		if (req.http.bytes) {
+			set resp.http.bytes =
+			    std.integer(bytes=std.bytes(req.http.bytes));
+		}
+		if (req.http.duration) {
+			set resp.http.duration =
+			    std.integer(duration=std.duration(req.http.duration));
+		}
+		if (req.http.real) {
+			set resp.http.real =
+			    std.integer(real=std.real(req.http.real));
+		}
+		if (req.http.time) {
+			set resp.http.time =
+			    std.integer(time=std.time(req.http.time));
+		}
+		if (req.http.err1) {
+			set resp.http.time = std.integer();
+		}
+		if (req.http.err2) {
+			set resp.http.time = std.integer(real=1.0, bool=true);
+		}
 	}
 } -start
 
 client c1 {
-	txreq -hdr "foo: 1"
+	txreq -hdr "foo: 1" \
+	      -hdr "bool: whatever" \
+	      -hdr "bytes: 1b" \
+	      -hdr "duration: 1s" \
+	      -hdr "real: 1.9" \
+	      -hdr "time: Thu, 01 Jan 1970 00:00:01 GMT"
 	rxresp
 	expect resp.http.gtzero == true
 	expect resp.http.ltzero == false
 	expect resp.http.iszero == false
 	expect resp.http.converted == 1
+	expect resp.http.bool == 1
+	expect resp.http.duration == 1
+	expect resp.http.bytes == 1
+	expect resp.http.real == 1
+	expect resp.http.time == 1
 
 	txreq -hdr "foo: -1"
 	rxresp
@@ -38,10 +79,71 @@ client c1 {
 	expect resp.http.iszero == true
 	expect resp.http.converted == 0
 
+	# VCL_INT_MAX
+	txreq -hdr "foo: 9007199254740991" \
+	      -hdr "bytes: 9007199254740991b" \
+	      -hdr "duration: 9007199254740991s" \
+	      -hdr "real: 9007199254740991" \
+	      -hdr "time: 9007199254740991"
+	rxresp
+	expect resp.http.gtzero == true
+	expect resp.http.ltzero == false
+	expect resp.http.iszero == false
+	expect resp.http.converted == 9007199254740991
+	expect resp.http.bytes == 9007199254740991
+	expect resp.http.duration == 9007199254740991
+	expect resp.http.real == 9007199254740991
+	expect resp.http.time == 9007199254740991
+
+	# VCL_INT_MIN
+	txreq -hdr "foo: -9007199254740991" \
+	      -hdr "duration: -9007199254740991s" \
+	      -hdr "real: -9007199254740991"
+	rxresp
+	expect resp.http.gtzero == false
+	expect resp.http.ltzero == true
+	expect resp.http.iszero == false
+	expect resp.http.converted == -9007199254740991
+	expect resp.http.duration == -9007199254740991
+	expect resp.http.real == -9007199254740991
+
 	txreq -hdr "foo: bar"
 	rxresp
 	expect resp.http.gtzero == false
 	expect resp.http.ltzero == false
 	expect resp.http.iszero == true
 	expect resp.http.converted == 0
+
+	txreq -hdr "foo: 9007199254740992"
+	rxresp
+	expect resp.http.converted == 0
+
+	txreq -hdr "foo: -9007199254740992"
+	rxresp
+	expect resp.http.converted == 0
+} -run
+client c1 {
+	txreq -hdr "nofb: 9007199254740992"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "nofb: -9007199254740992"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "nofb: x"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "err1: 1"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "err2: 1"
+	rxresp
+	expect resp.status == 503
 } -run
diff --git a/bin/varnishtest/tests/m00015.vtc b/bin/varnishtest/tests/m00015.vtc
index 1845fa7ce..3e7974958 100644
--- a/bin/varnishtest/tests/m00015.vtc
+++ b/bin/varnishtest/tests/m00015.vtc
@@ -1,4 +1,4 @@
-varnishtest "Test vmod_std.real conversion"
+varnishtest "Test std.real() conversion and std.round"
 
 server s1 {
 	rxreq
@@ -17,6 +17,8 @@ varnish v1 -vcl+backend {
 		set resp.http.t5 = std.real("1.0 ", 0.0);
 		set resp.http.t6 = std.real(" 1.0", 0.0);
 		set resp.http.t7 = std.real(" 1.0 ", 0.0);
+		set resp.http.t8 = std.round(42.42);
+		set resp.http.t9 = std.round(-42.42);
 	}
 } -start
 
@@ -32,4 +34,112 @@ client c1 {
 	expect resp.http.t5 == 1.000
 	expect resp.http.t6 == 1.000
 	expect resp.http.t7 == 1.000
+	expect resp.http.t8 == 42.000
+	expect resp.http.t9 == -42.000
+} -run
+
+varnish v1 -vcl+backend {
+	import std;
+
+	sub vcl_deliver {
+		set resp.http.converted = std.real(req.http.foo, 0);
+
+		if (req.http.nofb) {
+			set resp.http.nofb = std.real(req.http.foo);
+		}
+
+		if (req.http.bool) {
+			set resp.http.bool =
+			    std.real(bool=req.http.bool != "");
+		}
+		if (req.http.bytes) {
+			set resp.http.bytes =
+			    std.real(bytes=std.bytes(req.http.bytes));
+		}
+		if (req.http.duration) {
+			set resp.http.duration =
+			    std.real(duration=std.duration(req.http.duration));
+		}
+		if (req.http.integer) {
+			set resp.http.integer =
+			    std.real(integer=std.integer(req.http.integer));
+		}
+		if (req.http.time) {
+			set resp.http.time =
+			    std.real(time=std.time(req.http.time));
+		}
+	}
+}
+
+client c1 {
+	txreq -hdr "foo: 1" \
+	      -hdr "bool: whatever" \
+	      -hdr "bytes: 1b" \
+	      -hdr "duration: 1s" \
+	      -hdr "integer: 1" \
+	      -hdr "time: Thu, 01 Jan 1970 00:00:01 GMT"
+	rxresp
+	expect resp.http.converted == 1.000
+	expect resp.http.bool == 1.000
+	expect resp.http.duration == 1.000
+	expect resp.http.bytes == 1.000
+	expect resp.http.integer == 1.000
+	expect resp.http.time == 1.000
+
+	txreq -hdr "foo: -1"
+	rxresp
+	expect resp.http.converted == -1.000
+
+	txreq -hdr "foo: 0"
+	rxresp
+	expect resp.http.converted == 0.000
+
+	# VCL_INT_MAX
+	txreq -hdr "foo: 9007199254740991" \
+	      -hdr "bytes: 9007199254740991b" \
+	      -hdr "duration: 9007199254740991s" \
+	      -hdr "integer: 9007199254740991" \
+	      -hdr "time: 9007199254740991"
+	rxresp
+	expect resp.http.converted == 9007199254740991.000
+	expect resp.http.bytes == 9007199254740991.000
+	expect resp.http.duration == 9007199254740991.000
+	expect resp.http.integer == 9007199254740991.000
+	expect resp.http.time == 9007199254740991.000
+
+	# VCL_INT_MIN
+	txreq -hdr "foo: -9007199254740991" \
+	      -hdr "duration: -9007199254740991s" \
+	      -hdr "integer: -9007199254740991"
+	rxresp
+	expect resp.http.converted == -9007199254740991.000
+	expect resp.http.duration == -9007199254740991.000
+	expect resp.http.integer == -9007199254740991.000
+
+	txreq -hdr "foo: bar"
+	rxresp
+	expect resp.http.converted == 0.000
+
+	txreq -hdr "foo: 9007199254740992"
+	rxresp
+	expect resp.http.converted == 9007199254740992.000
+
+	txreq -hdr "foo: -9007199254740992"
+	rxresp
+	expect resp.http.converted == -9007199254740992.000
+} -run
+client c1 {
+	txreq -hdr "nofb: NAN"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "nofb: -INF"
+	rxresp
+	expect resp.status == 503
+} -run
+client c1 {
+	txreq -hdr "nofb: x"
+	rxresp
+	expect resp.status == 503
 } -run
diff --git a/bin/varnishtest/tests/m00020.vtc b/bin/varnishtest/tests/m00020.vtc
index eb2eb0f5b..fd61e11fb 100644
--- a/bin/varnishtest/tests/m00020.vtc
+++ b/bin/varnishtest/tests/m00020.vtc
@@ -17,47 +17,74 @@ varnish v1 -vcl+backend {
 		if (std.time(req.http.x-date, now) > now + 1y) {
 			set resp.http.x-future = 1;
 		}
+		if (req.http.real) {
+			if (resp.http.x-date != "" +
+			    std.time(real=std.real(req.http.real))) {
+				return(synth(503));
+			}
+		}
+		if (req.http.integer) {
+			if (resp.http.x-date != "" +
+			    std.time(integer=std.integer(req.http.integer))) {
+				return(synth(503));
+			}
+		}
 	}
 } -start
 
 client c1 {
-	txreq -hdr "X-Date: Mon, 20 Dec 2010 00:00:00 GMT"
+	txreq -hdr "X-Date: Mon, 20 Dec 2010 00:00:00 GMT" \
+	      -hdr "real: 1292803200.0" \
+	      -hdr "integer: 1292803200"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-past == 1
 	expect resp.http.x-date == "Mon, 20 Dec 2010 00:00:00 GMT"
 
-	txreq -hdr "X-Date: Monday, 20-Dec-30 00:00:00 GMT"
+	# invalid date
+	txreq -hdr "X-Date: Monday, 20-Dec-30 00:00:00 GMT" \
+	      -hdr "real: 1923955200.0" \
+	      -hdr "integer: 1923955200"
 	rxresp
+	expect resp.status == 503
 	expect resp.http.x-past == <undef>
 	expect resp.http.x-future == <undef>
 
 	txreq -hdr "X-Date: Monday, 30-Feb-15 00:00:00 GMT"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-past == <undef>
 	expect resp.http.x-future == <undef>
 
-	txreq -hdr "X-Date: Friday, 20-Dec-30 00:00:00 GMT"
+	txreq -hdr "X-Date: Friday, 20-Dec-30 00:00:00 GMT" \
+	      -hdr "real: 1923955200.0" \
+	      -hdr "integer: 1923955200"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-future == 1
 	expect resp.http.x-date == "Fri, 20 Dec 2030 00:00:00 GMT"
 
 	txreq -hdr "X-Date: Mon Dec 20 00:00:00 2010"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-past == 1
 	expect resp.http.x-date == "Mon, 20 Dec 2010 00:00:00 GMT"
 
 	txreq -hdr "X-Date: 2030-12-20T00:00:00"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-future == 1
 	expect resp.http.x-date == "Fri, 20 Dec 2030 00:00:00 GMT"
 
 	txreq -hdr "X-Date: 1292803200.00"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-past == 1
 	expect resp.http.x-date == "Mon, 20 Dec 2010 00:00:00 GMT"
 
 	txreq -hdr "X-Date: 1923955200"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-future == 1
 	expect resp.http.x-date == "Fri, 20 Dec 2030 00:00:00 GMT"
 
@@ -68,66 +95,79 @@ client c1 {
 	# leapsecond
 	txreq -hdr "X-Date: Mon, 20 Dec 2010 00:00:60 GMT"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-date == "Mon, 20 Dec 2010 00:00:59 GMT"
 	delay .1
 
 	# Range tests
 	txreq -hdr "X-Date: Mon, 20 Dec 2010 00:00:61 GMT"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-date != "Mon, 20 Dec 2010 00:00:61 GMT"
 	delay .1
 
 	txreq -hdr "X-Date: Mon, 20 Dec 2010 00:60:00 GMT"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-date != "Mon, 20 Dec 2010 00:60:00 GMT"
 	delay .1
 
 	txreq -hdr "X-Date: Mon, 20 Dec 2010 24:00:00 GMT"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-date != "Mon, 20 Dec 2010 24:00:00 GMT"
 	delay .1
 
 	txreq -hdr "X-Date: Tue, 20 Dec 2010 00:00:00 GMT"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-date != "Tue, 20 Dec 2010 00:00:00 GMT"
 	delay .1
 
 	txreq -hdr "X-Date: Mon, 29 Feb 2010 00:00:00 GMT"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-date != "Mon, 29 Feb 2010 00:00:00 GMT"
 	delay .1
 
 	txreq -hdr "X-Date: Wed, 29 Feb 2012 00:00:00 GMT"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-date == "Wed, 29 Feb 2012 00:00:00 GMT"
 	delay .1
 
 	txreq -hdr "X-Date: 2010-13-20T00:00:00"
 	rxresp
+	expect resp.status == 200
 	delay .1
 
 	txreq -hdr "X-Date: Sun 31 Dec 1899 23:59:59 GMT"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-date != "Sun 31 Dec 1899 23:59:59 GMT"
 	delay .1
 
 	# White space etc.
 	txreq -hdr "X-Date: z Wed, 29 Feb 2012 00:00:00 GMT"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-date == "Wed, 29 Feb 2012 00:00:00 GMT"
 	delay .1
 
 	txreq -hdr "X-Date: Wedx 29 Feb 2012 00:00:00 GMT"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-date != "Wed, 29 Feb 2012 00:00:00 GMT"
 	delay .1
 
 	txreq -hdr "X-Date: Wed, 29 Feb 2012 00:00:00 GMT x"
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-date != "Wed, 29 Feb 2012 00:00:00 GMT"
 	delay .1
 
 	txreq
 	rxresp
+	expect resp.status == 200
 	expect resp.http.x-date != <undef>
 } -run
diff --git a/lib/libvmod_std/vmod.vcc b/lib/libvmod_std/vmod.vcc
index b3d994618..bf2396c21 100644
--- a/lib/libvmod_std/vmod.vcc
+++ b/lib/libvmod_std/vmod.vcc
@@ -157,38 +157,96 @@ Examples::
 	std.collect(req.http.accept);
 	std.collect(req.http.cookie, "; ");
 
-$Function DURATION duration(STRING s, DURATION fallback)
+$Function DURATION duration([STRING s], [DURATION fallback],
+    [REAL real], [INT integer])
 
-Converts the string *s* to seconds. *s* must be quantified with ``ms``
-(milliseconds), ``s`` (seconds), ``m`` (minutes), ``h`` (hours), ``d``
-(days), ``w`` (weeks) or ``y`` (years) units. If conversion fails,
-*fallback* will be returned.
+Returns a DURATION from a STRING, REAL or INT argument.
 
-Example::
+For a STRING *s* argument, *s* must be quantified by ``ms``
+(milliseconds), ``s`` (seconds), ``m`` (minutes), ``h`` (hours),``d``
+(days), ``w`` (weeks) or ``y`` (years) units.
+
+*real* and *integer* arguments are taken as seconds.
+
+If the conversion of an *s* argument fails, *fallback* will be
+returned if provided, or a VCL failure will be triggered.
 
+Conversions from *real* and *integer* arguments never fail.
+
+Only one of the *s*, *real* or *integer* arguments may be given or a VCL
+failure will be triggered.
+
+Examples::
 	set beresp.ttl = std.duration("1w", 3600s);
+	set beresp.ttl = std.duration(real=1.5);
+	set beresp.ttl = std.duration(integer=10);
 
-$Function BYTES bytes(STRING s, BYTES fallback)
+$Function BYTES bytes([STRING s], [BYTES fallback], [REAL real], [INT integer])
 
-Converts the string *s* to bytes. *s* can be quantified with a
-multiplier (``k``, ``m``, ``g``, ``t``, ``p``).  If conversion fails,
-*fallback* will be returned.
+Returns BYTES from a STRING, REAL or INT argument.
 
-Example::
+A STRING *s* argument can be quantified with a multiplier (``k``
+(kilo), ``m`` (mega), ``g`` (giga), ``t`` (tera) or ``p`` (peta)).
 
-	std.cache_req_body(std.bytes(something.somewhere, 10K));
+*real* and *integer* arguments are taken as bytes.
+
+If the conversion of an *s* argument fails, *fallback* will be
+returned if provided, or a VCL failure will be triggered.
 
-$Function INT integer(STRING s, INT fallback)
+Other conversions may fail if the argument can not be represented,
+because it is negative, too small or too large. Again, *fallback* will
+be returned if provided, or a VCL failure will be triggered.
 
-Converts the string *s* to an integer. If conversion fails, *fallback*
-will be returned.
+*real* arguments will be rounded down.
+
+Only one of the *s*, *real* or *integer* arguments may be given or a VCL
+failure will be triggered.
 
 Example::
+	std.cache_req_body(std.bytes(something.somewhere, 10K));
+	std.cache_req_body(std.bytes(integer=10*1024));
+	std.cache_req_body(std.bytes(real=10.0*1024));
+
+$Function INT integer([STRING s], [INT fallback],
+    [BOOL bool], [BYTES bytes], [DURATION duration], [REAL real],
+    [TIME time])
+
+Returns an INT from a STRING, BOOL or other quantity.
+
+If the conversion of an *s* argument fails, *fallback* will be
+returned if provided, or a VCL failure will be triggered.
+
+A *bool* argument will be returned as 0 for ``false`` and 1 for
+``true``. This conversion will never fail.
+
+For a *bytes* argument, the number of bytes will be returned.  This
+conversion will never fail.
+
+A *duration* argument will be rounded down to the number of seconds
+and returned.
+
+A *real* argument will be rounded down and returned.
+
+For a *time* argument, the number of seconds since the UNIX epoch
+(1970-01-01 00:00:00 UTC) will be returned.
+
+*duration*, *real* and *time* conversions may fail if the argument can
+not be represented because it is too small or too large. If so,
+*fallback* will be returned if provided, or a VCL failure will be
+triggered.
+
+Only one of the *s*, *bool*, *bytes*, *duration*, *real* or *time*
+arguments may be given or a VCL failure will be triggered.
+
+Examples::
 
 	if (std.integer(req.http.foo, 0) > 5) {
 		...
 	}
 
+	set resp.http.answer = std.integer(real=126.42/3);
+
+
 $Function IP ip(STRING s, IP fallback, BOOL resolve = 1)
 
 Converts the string *s* to the first IP number returned by
@@ -205,10 +263,31 @@ Example::
 		...
 	}
 
-$Function REAL real(STRING s, REAL fallback)
+$Function REAL real([STRING s], [REAL fallback], [INT integer],
+    [BOOL bool], [BYTES bytes], [DURATION duration],
+    [TIME time])
 
-Converts the string *s* to a real. If conversion fails, *fallback*
-will be returned.
+Returns a REAL from a STRING, BOOL or other quantity.
+
+If the conversion of an *s* argument fails, *fallback* will be
+returned if provided, or a VCL failure will be triggered.
+
+A *bool* argument will be returned as 0.0 for ``false`` and 1.0 for
+``true``.
+
+For a *bytes* argument, the number of bytes will be returned.
+
+For a *duration* argument, the number of seconds will be returned.
+
+An *integer* argument will be returned as a REAL.
+
+For a *time* argument, the number of seconds since the UNIX epoch
+(1970-01-01 00:00:00 UTC) will be returned.
+
+None of these conversions other than *s* will fail.
+
+Only one of the *s*, *integer*, *bool*, *bytes*, *duration* or *time*
+arguments may be given or a VCL failure will be triggered.
 
 Example::
 
@@ -216,8 +295,19 @@ Example::
 		...
 	}
 
+$Function REAL round(REAL r)
+
+Rounds the real *r* to the nearest integer, but round halfway cases
+away from zero (see `round(3)`).
+
 $Function INT real2integer(REAL r, INT fallback)
 
+**DEPRECATED**: This function will be removed in a future version of
+varnish, use `vmod_std.integer`_ with a *real* argument and the
+`vmod_std.round`_ function instead, for example::
+
+	std.integer(real=std.round(...), fallback=...)
+
 Rounds the real *r* to the nearest integer, but round halfway cases
 away from zero (see `round(3)`). If conversion fails, *fallback* will
 be returned.
@@ -230,6 +320,12 @@ Examples::
 
 $Function TIME real2time(REAL r, TIME fallback)
 
+**DEPRECATED**: This function will be removed in a future version of
+varnish, use `vmod_std.time`_ with a *real* argument and the
+`vmod_std.round`_ function instead, for example::
+
+	std.time(real=std.round(...), fallback=...)
+
 Rounds the real *r* to the nearest integer (see
 `vmod_std.real2integer`_) and returns the corresponding time when
 interpreted as a unix epoch. If conversion fails, *fallback* will be
@@ -241,6 +337,12 @@ Example::
 
 $Function INT time2integer(TIME t, INT fallback)
 
+**DEPRECATED**: This function will be removed in a future version of
+varnish, use `vmod_std.integer`_ with a *time* argument instead, for
+example::
+
+	std.integer(time=..., fallback=...)
+
 Converts the time *t* to a integer. If conversion fails,
 *fallback* will be returned.
 
@@ -250,6 +352,12 @@ Example::
 
 $Function REAL time2real(TIME t, REAL fallback)
 
+**DEPRECATED**: This function will be removed in a future version of
+varnish, use `vmod_std.real`_ with a *time* argument instead, for
+example::
+
+	std.real(time=..., fallback=...)
+
 Converts the time *t* to a real. If conversion fails, *fallback* will
 be returned.
 
@@ -325,12 +433,11 @@ Example::
 This will check if the content of ``req.http.restrict`` occurs
 anywhere in ``req.url``.
 
-$Function TIME time(STRING s, TIME fallback)
+$Function TIME time([STRING s], [TIME fallback], [REAL real], [INT integer])
 
-Converts the string *s* to a time. If conversion fails, *fallback*
-will be returned.
+Returns a TIME from a STRING, REAL or INT argument.
 
-Supported formats::
+For a STRING *s* argument, the following formats are supported::
 
 	"Sun, 06 Nov 1994 08:49:37 GMT"
 	"Sunday, 06-Nov-94 08:49:37 GMT"
@@ -339,12 +446,22 @@ Supported formats::
 	"784111777.00"
 	"784111777"
 
-Example::
+*real* and *integer* arguments are taken as seconds since the epoch.
+
+If the conversion of an *s* argument fails or a negative *real* or
+*integer* argument is given, *fallback* will be returned if provided,
+or a VCL failure will be triggered.
+
+Examples::
 
 	if (std.time(resp.http.last-modified, now) < now - 1w) {
 		...
 	}
 
+	if (std.time(int=2147483647) < now - 1w) {
+		...
+	}
+
 $Function STRING getenv(STRING name)
 
 Return environment variable *name* or the empty string. See `getenv(3)`.
diff --git a/lib/libvmod_std/vmod_std_conversions.c b/lib/libvmod_std/vmod_std_conversions.c
index ed85bed0f..c20402b1c 100644
--- a/lib/libvmod_std/vmod_std_conversions.c
+++ b/lib/libvmod_std/vmod_std_conversions.c
@@ -44,47 +44,139 @@
 #include "vtim.h"
 #include "vcc_if.h"
 
+static inline int onearg(VRT_CTX, const char *f, int nargs)
+{
+	if (nargs == 1)
+		return (1);
+	VRT_fail(ctx, "std.%s: %s arguments", f,
+	    nargs > 1 ? "too many" : "not enough");
+	return (0);
+}
+
+/*
+ * not handling real arg isfinite() / nan() : caller error
+ * always trunc, never round
+ */
+
 VCL_DURATION v_matchproto_(td_std_duration)
-vmod_duration(VRT_CTX, VCL_STRING p, VCL_DURATION d)
+vmod_duration(VRT_CTX, struct VARGS(duration) *a)
 {
 	double r;
+	int nargs;
 
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
-	r = VNUM_duration(p);
-	return (isnan(r) ? d : r);
+
+	nargs = a->valid_s + a->valid_real + a->valid_integer;
+
+	if (! onearg(ctx, "duration", nargs))
+		return (0);
+
+	if (a->valid_real)
+		return ((VCL_DURATION)a->real);
+	if (a->valid_integer)
+		return ((VCL_DURATION)a->integer);
+
+	assert(a->valid_s);
+
+	r = VNUM_duration(a->s);
+
+	if (! isnan(r))
+		return (r);
+
+	if (a->valid_fallback)
+		return (a->fallback);
+
+	VRT_fail(ctx, "std.duration: conversion failed");
+	return (0);
 }
 
 VCL_BYTES v_matchproto_(td_std_bytes)
-vmod_bytes(VRT_CTX, VCL_STRING p, VCL_BYTES d)
+vmod_bytes(VRT_CTX, struct VARGS(bytes) *a)
 {
 	uintmax_t r;
+	VCL_BYTES b;
+	VCL_REAL rr;
+	int nargs;
 
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
-	if (VNUM_2bytes(p, &r, 0) != NULL)
-		return (d);
-	return (r);
+
+	nargs = a->valid_s + a->valid_real + a->valid_integer;
+
+	if (! onearg(ctx, "bytes", nargs))
+		return (0);
+
+	b = -1;
+	if (a->valid_s) {
+		if (VNUM_2bytes(a->s, &r, 0) == NULL &&
+		    r <= VCL_BYTES_MAX)
+			b = (VCL_BYTES)r;
+	} else if (a->valid_real) {
+		rr = trunc(a->real);
+		if (rr <= (VCL_REAL)VCL_BYTES_MAX)
+			b = (VCL_BYTES)rr;
+	} else if (a->valid_integer) {
+		b = (VCL_BYTES)a->integer;
+	} else {
+		INCOMPL();
+	}
+
+	if (b >= 0)
+		return (b);
+
+	if (a->valid_fallback)
+		return (a->fallback);
+
+	VRT_fail(ctx, "std.bytes: conversion failed");
+	return (0);
 }
 
 VCL_INT v_matchproto_(td_std_integer)
-vmod_integer(VRT_CTX, VCL_STRING p, VCL_INT i)
+vmod_integer(VRT_CTX, struct VARGS(integer) *a)
 {
 	const char *e;
 	double r;
+	int nargs;
 
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
 
-	if (p == NULL)
-		return (i);
+	nargs = a->valid_s + a->valid_bool + a->valid_bytes +
+	    a->valid_duration + a->valid_real + a->valid_time;
+
+	if (! onearg(ctx, "integer", nargs))
+		return (0);
+
+	r = NAN;
+	if (a->valid_bool) {
+		return (!! a->bool);
+	} else if (a->valid_bytes) {
+		return (a->bytes);
+	} else if (a->valid_s) {
+		if (a->s) {
+			r = VNUMpfx(a->s, &e);
+			if (e != NULL)
+				r = NAN;
+		}
+	} else if (a->valid_duration) {
+		r = a->duration;
+	} else if (a->valid_real) {
+		r = a->real;
+	} else if (a->valid_time) {
+		r = a->time;
+	} else {
+		INCOMPL();
+	}
 
-	r = VNUMpfx(p, &e);
-	if (isnan(r) || e != NULL)
-		return (i);
+	if (! isnan(r)) {
+		r = trunc(r);
+		if (r >= VCL_INT_MIN && r <= VCL_INT_MAX)
+			return ((VCL_INT)r);
+	}
 
-	r = trunc(r);
-	if (r > VCL_INT_MAX || r < VCL_INT_MIN)
-		return (i);
+	if (a->valid_fallback)
+		return (a->fallback);
 
-	return ((VCL_INT)r);
+	VRT_fail(ctx, "std.integer: conversion failed");
+	return (0);
 }
 
 VCL_IP
@@ -133,23 +225,54 @@ vmod_ip(VRT_CTX, VCL_STRING s, VCL_IP d, VCL_BOOL n)
 }
 
 VCL_REAL v_matchproto_(td_std_real)
-vmod_real(VRT_CTX, VCL_STRING p, VCL_REAL d)
+vmod_real(VRT_CTX, struct VARGS(real) *a)
 {
-	double r;
+	VCL_REAL r;
+	int nargs;
 
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
 
-	if (p == NULL)
-		return (d);
+	nargs = a->valid_s + a->valid_integer + a->valid_bool + a->valid_bytes +
+	    a->valid_duration + a->valid_time;
 
-	r = VNUM(p);
+	if (! onearg(ctx, "real", nargs))
+		return (0);
 
-	if (isnan(r))
-		return (d);
+	if (a->valid_integer)
+		return ((VCL_REAL)a->integer);
+	else if (a->valid_bool)
+		return ((VCL_REAL)(!! a->bool));
+	else if (a->valid_bytes)
+		return ((VCL_REAL)a->bytes);
+	else if (a->valid_duration)
+		return ((VCL_REAL)a->duration);
+	else if (a->valid_time)
+		return ((VCL_REAL)a->time);
 
-	return (r);
+	assert(a->valid_s);
+
+	r = NAN;
+	if (a->s != NULL)
+		r = VNUM(a->s);
+
+	if (!isnan(r))
+		return (r);
+
+	if (a->valid_fallback)
+		return (a->fallback);
+
+	VRT_fail(ctx, "std.real: conversion failed");
+	return (0);
+}
+
+VCL_REAL v_matchproto_(td_std_round)
+vmod_round(VRT_CTX, VCL_REAL r)
+{
+	(void) ctx;
+	return (round(r));
 }
 
+
 VCL_INT v_matchproto_(td_std_real2integer)
 vmod_real2integer(VRT_CTX, VCL_REAL r, VCL_INT i)
 {
@@ -199,14 +322,39 @@ vmod_time2real(VRT_CTX, VCL_TIME t, VCL_REAL r)
 }
 
 VCL_TIME v_matchproto_(td_std_time)
-vmod_time(VRT_CTX, VCL_STRING p, VCL_TIME d)
+vmod_time(VRT_CTX, struct VARGS(time)* a)
 {
 	double r;
+	int nargs;
 
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
 
-	r = VTIM_parse(p);
-	if (r)
-		return (r);
-	return (vmod_real(ctx, p, d));
+	nargs = a->valid_s + a->valid_real + a->valid_integer;
+
+	if (! onearg(ctx, "time", nargs))
+		return (0);
+
+	if (a->valid_integer)
+		return ((VCL_REAL)a->integer);
+	else if (a->valid_real)
+		return ((VCL_REAL)a->real);
+
+	assert(a->valid_s);
+
+	if (a->s) {
+		r = VTIM_parse(a->s);
+		if (r)
+			return (r);
+
+		r = VNUM(a->s);
+
+		if (!isnan(r) && r > 0)
+			return (r);
+	}
+
+	if (a->valid_fallback)
+		return (a->fallback);
+
+	VRT_fail(ctx, "std.time: conversion failed");
+	return (0);
 }


More information about the varnish-commit mailing list