<div dir="ltr">Below are some thoughts and a prototype patch for expanding object support. Today, objects are limited to global objects which have a lifetime of the entire VCL. I feel its useful to have objects which can be created during the request and their scope is limited to that request. When the request is done, the objects are finalized.<div><br></div><div>The driver for this is creating a new curl/http vmod (I have several other vmods in mind which would benefit from this). When making curl requests from VCL, you may want to have multiple outstanding (async) requests, so we need to have the ability to encapsulate these requests in isolated, independent, and request scoped objects. Another use case is creating simple type objects, like VCL_STRING, VCL_INT, and VCL_BLOB. We can wrap these types into an object and we now have VCL variables which don't require conversion into headers and we can define them on the fly per request. This will likely open the door for some very interesting VCL functionality :)</div><div><br></div><div>Here is a VCL snippet which compiles and works with the patch and uses my new libvmod_types [0].</div><div><br></div><div>---</div><div><font face="monospace, monospace">import types;</font></div><div><font face="monospace, monospace"><br></font></div><div><div><font face="monospace, monospace">sub vcl_init</font></div><div><font face="monospace, monospace">{</font></div><div><font face="monospace, monospace">  //global objects, these are unchanged from 4.0</font></div><div><font face="monospace, monospace">  new s = types.string("Hello!");</font></div><div><font face="monospace, monospace">  new reqs = types.integer(0);</font></div><div><font face="monospace, monospace">}</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">sub vcl_recv</font></div><div><font face="monospace, monospace">{</font></div><div><font face="monospace, monospace">  //new req scoped objects</font></div><div><font face="monospace, monospace">  new req.var.slocal = types.string("Request scoped string");</font></div><div><font face="monospace, monospace">  new req.var.s2 = types.string("request string two");</font></div><div><font face="monospace, monospace">  new req.var.count = types.integer(1);</font></div><div><font face="monospace, monospace">}</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">sub vcl_backend_fetch</font></div><div><font face="monospace, monospace">{</font></div><div><font face="monospace, monospace">  //new bereq scoped objects</font></div><div><font face="monospace, monospace">  new bereq.var.sbe = types.string("berequest string v1");</font></div><div><font face="monospace, monospace">  set bereq.http.sbe = bereq.var.sbe.value();</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">  bereq.var.sbe.set("berequest string v2");</font></div><div><font face="monospace, monospace">  set bereq.http.sbe2 = bereq.var.sbe.value();</font></div><div><font face="monospace, monospace">}</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">sub vcl_deliver</font></div><div><font face="monospace, monospace">{</font></div><div><font face="monospace, monospace">  //referencing a mix of global and req scoped objects</font></div><div><font face="monospace, monospace">  set resp.http.X-s = s.value();</font></div><div><font face="monospace, monospace">  set resp.http.X-s-length = s.length();</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">  set resp.http.X-slocal = req.var.slocal.value();</font></div><div><font face="monospace, monospace">  set resp.http.X-slocal-length = req.var.slocal.length();</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">  req.var.count.increment(10);</font></div><div><font face="monospace, monospace">  set resp.http.count = req.var.count.value();</font></div><div><font face="monospace, monospace">  set resp.http.reqs = reqs.increment_get(1);</font></div><div><font face="monospace, monospace">}</font></div></div><div>---</div><div><br></div><div>The theoretical curl/http example:</div><div><br></div><div>---</div><div><font face="monospace, monospace">import http;</font></div><div><font face="monospace, monospace"><br></font></div><div><div><font face="monospace, monospace">sub vcl_recv</font></div><div><font face="monospace, monospace">{</font></div><div><font face="monospace, monospace">  //http request #1</font></div><div><font face="monospace, monospace">  new req.var.h1 = http.request();</font></div><div><font face="monospace, monospace">  req.var.h1.set_header("foo", "bar");</font></div><div><font face="monospace, monospace">  req.var.h1.set_url("POST", "<a href="http://host1/blah?ok=true">http://host1/blah?ok=true</a>");</font></div><div><font face="monospace, monospace">  req.var.h1.send();</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">  //http request #2 (we dont read it so its async)</font></div><div><font face="monospace, monospace">  new req.var.h2 = http.request();</font></div><div><font face="monospace, monospace">  req.var.h2.set_url("GET", "<a href="http://host2/ping">http://host2/ping</a>");</font></div><div><font face="monospace, monospace">  req.var.h2.send();</font></div><div><font face="monospace, monospace">}</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">sub vcl_deliver</font></div><div><font face="monospace, monospace">{</font></div><div><font face="monospace, monospace">  //reference and read http request #1 and block for result</font></div><div><font face="monospace, monospace">  set resp.http.X-test-response-code = req.var.h1.get_response_code();</font></div><div><font face="monospace, monospace">}</font></div></div><div>---</div><div><br></div><div>I left the legacy global objects alone in code and syntax. I introduced 2 new variable name scopes: req.var.* and bereq.var.*. This is completely cosmetic as these variables can still be request scoped without the (be)req.var prefix. However, the reason for adding it is to give the user some kind of indication that their variable is tied to a frontend, backend, or global scope. Otherwise I have the feeling having a bunch of un-prefixed variables throwing vcc scope errors when used incorrectly will be confusing.</div><div><br></div><div>Also, the implementation is fairly simple because I piggybacked on the vmod/vrt priv_task implementation. Request scoped objects are basically given a shimmed struct vmod_priv. I had to jump thru a few small hoops in vcc code to get the priv->priv to cast into an actual struct that the VMOD expects. This may or may not be related to VIP#1, but it would be cleaner to move objects to something more priv like than trying to pass in an explicit struct. However, for the patch, I kept the object interface the same and made use of the previously mentioned vcc/vrt shims.</div><div><br></div><div>The patch is enough to have the examples work and give you guys an idea of how it would work. I wanted to get some feedback before spending more time on this. Its based off of this commit [1], so feel free to comment on github if you want.</div><div><br></div><div><br></div><div>[0] <a href="https://github.com/rezan/libvmod-types">https://github.com/rezan/libvmod-types</a></div><div>[1] <a href="https://github.com/rezan/varnish-cache/commit/b547bd9ad2fca9db1ef17ee73b8e9b7df9950c34">https://github.com/rezan/varnish-cache/commit/b547bd9ad2fca9db1ef17ee73b8e9b7df9950c34</a></div><div><br></div><div>Thanks!</div><div><br clear="all"><div><div class="gmail_signature"><div dir="ltr">--<br>Reza Naghibi<br>Varnish Software</div></div></div>
</div></div>