Commit d54c3149 authored by Lasse Karstensen's avatar Lasse Karstensen

Big rework of example VCL testing.

parent a33cf2a4
tests/99*
tests/snippet-*.vtc
......@@ -26,12 +26,14 @@ content is dependant on this header.
Everything works out of the box from Varnish' perspective.
.. 71-example1-start
.. startsnippet-example1
VCL::
include "devicedetect.vcl";
sub vcl_recv { call devicedetect; }
sub vcl_recv {
call devicedetect;
}
# req.http.X-UA-Device is copied by Varnish into bereq.http.X-UA-Device
# so, this is a bit conterintuitive. The backend creates content based on the normalized User-Agent,
......@@ -60,7 +62,7 @@ VCL::
}
}
.. 71-example1-end
.. endsnippet-example1
Example 2: Normalize the User-Agent string
......@@ -83,12 +85,19 @@ This works if you don't need the original header for anything on the backend. A
possible use for this is for CGI scripts where only a small set of predefined
headers are (by default) available for the script.
.. 72-example2-start
.. req: txreq -hdr "User-Agent: Mozilla/5.0 (Linux; U; Android 2.2; nb-no; HTC Desire Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
.. req: rxresp
.. req: expect resp.http.X-UA-Device == "mobile-android"
.. req: expect resp.http.Vary == "User-Agent"
.. startsnippet-example2
VCL::
include "devicedetect.vcl";
sub vcl_recv { call devicedetect; }
sub vcl_recv {
call devicedetect;
}
# override the header before it is sent to the backend
sub vcl_backend_fetch { if (bereq.http.X-UA-Device) { set bereq.http.User-Agent = bereq.http.X-UA-Device; } }
......@@ -110,7 +119,7 @@ VCL::
}
}
.. 72-example2-end
.. endsnippet-example2
Example 3: Add the device class as a GET query parameter
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
......@@ -121,12 +130,14 @@ If everything else fails, you can add the device type as a GET argument.
The client itself does not see this classification, only the backend request is changed.
.. 73-example3-start
.. startsnippet-example3
VCL::
include "devicedetect.vcl";
sub vcl_recv { call devicedetect; }
sub vcl_recv {
call devicedetect;
}
# do this after vcl_hash, so all Vary-ants can be purged in one go. (avoid ban()ing)
sub vcl_backend_fetch {
......@@ -166,18 +177,21 @@ VCL::
}
}
.. 73-example3-end
.. endsnippet-example3
Different backend for mobile clients
------------------------------------
If you have a different backend that serves pages for mobile clients, or any
special needs in VCL, you can use the X-UA-Device header like this::
special needs in VCL, you can use the X-UA-Device header like the following.
VCL::
vcl 4.0;
include "devicedetect.vcl";
backend mobile {
.host = "10.0.0.1";
.host = "192.0.2.10";
.port = "80";
}
......@@ -185,7 +199,7 @@ special needs in VCL, you can use the X-UA-Device header like this::
call devicedetect;
if (req.http.X-UA-Device ~ "^mobile" || req.http.X-UA-device ~ "^tablet") {
set req.backend = mobile;
set req.backend_hint = mobile;
}
}
......@@ -194,7 +208,11 @@ Redirecting mobile clients
If you want to redirect mobile clients you can use the following snippet.
.. 65-redir-mobile-start
.. req: txreq -req GET -url /foo -hdr "User-Agent: Mozilla/5.0 (Linux; U; Android 2.2; nb-no; HTC Desire Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1" -hdr "Host: example.com"
.. req: rxresp
.. req: expect resp.status == 302
.. req: expect resp.http.Location == "http://m.example.com/foo"
.. startsnippet-redir2
VCL::
......@@ -215,7 +233,7 @@ VCL::
}
}
.. 65-redir-mobile-end
.. endsnippet-redir2
Testing tools
......
#!/bin/bash -x
#
# To make sure the example VCL code is valid and runnable, actually store the auth copy in
# the varnishtest files. Use the magic of sed to put it into the documentation.
#
tmpfile=/tmp/foo$RANDOM
DOCUMENT=INSTALL.rst
transform() {
f=$1
# stuff to put into the section
sed -n '/varnish v1 -vcl+backend/,/start/p' tests/$f.vtc > $tmpfile
sed -i 's/${projectdir}\/..\///' $tmpfile
# remove first and last line of file. todo: learn more sed to do this more smoothly.
tail -n+2 $tmpfile | head -n-1 > $tmpfile.2; mv $tmpfile.2 $tmpfile
sed -e "/.. $f-start/ q" $DOCUMENT > $tmpfile.orig.top
echo -e "\nVCL::\n" >> $tmpfile.orig.top
sed -n "/.. $f-end/,\$p" $DOCUMENT >$tmpfile.orig.below
cat $tmpfile.orig.top $tmpfile $tmpfile.orig.below > $DOCUMENT
rm $tmpfile $tmpfile.orig.top $tmpfile.orig.below
}
transform 65-redir-mobile
transform 71-example1
transform 72-example2
transform 73-example3
varnishtest "Show example of per-device-class redirects."
server s1 {
rxreq
txresp
} -start
varnish v1 -vcl+backend {
include "${projectdir}/../devicedetect.vcl";
sub vcl_recv {
call devicedetect;
if (req.http.X-UA-Device ~ "^mobile" || req.http.X-UA-device ~ "^tablet") {
return(synth(750, "Moved Temporarily"));
}
}
sub vcl_synth {
if (resp.status == 750) {
set resp.http.Location = "http://m.example.com" + req.url;
set resp.status = 302;
return(deliver);
}
}
} -start
client c1 {
txreq -req GET -url /foo -hdr "User-Agent: Mozilla/5.0 (Linux; U; Android 2.2; nb-no; HTC Desire Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1" -hdr "Host: example.com"
rxresp
expect resp.status == 302
expect resp.http.Location == "http://m.example.com/foo"
} -run
client c2 {
txreq -req GET -url /foo -hdr "Host: example.com"
rxresp
expect resp.status == 200
expect resp.http.Location == <undef>
} -run
varnishtest "Test varnish-devicedetect"
server s1 {
rxreq
expect req.http.X-UA-Device == "mobile-android"
txresp
} -start
# this example shows how to normalize the user agent string seen by the backend.
varnish v1 -vcl+backend {
include "${projectdir}/../devicedetect.vcl";
sub vcl_recv {
call devicedetect;
}
# req.http.X-UA-Device is copied by Varnish into bereq.http.X-UA-Device
# so, this is a bit conterintuitive. The backend creates content based on the normalized User-Agent,
# but we use Vary on X-UA-Device so Varnish will use the same cached object for all U-As that map to
# the same X-UA-Device.
# If the backend does not mention in Vary that it has crafted special
# content based on the User-Agent (==X-UA-Device), add it.
# If your backend does set Vary: User-Agent, you may have to remove that here.
sub vcl_backend_response {
if (bereq.http.X-UA-Device) {
if (!beresp.http.Vary) { # no Vary at all
set beresp.http.Vary = "X-UA-Device";
} elseif (beresp.http.Vary !~ "X-UA-Device") { # add to existing Vary
set beresp.http.Vary = beresp.http.Vary + ", X-UA-Device";
}
}
# comment this out if you don't want the client to know your classification
set beresp.http.X-UA-Device = bereq.http.X-UA-Device;
}
# to keep any caches in the wild from serving wrong content to client #2 behind them, we need to
# transform the Vary on the way out.
sub vcl_deliver {
if ((req.http.X-UA-Device) && (resp.http.Vary)) {
set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent");
}
}
} -start
client c1 {
txreq -hdr "User-Agent: Mozilla/5.0 (Linux; U; Android 2.2; nb-no; HTC Desire Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
rxresp
expect resp.http.X-UA-Device == "mobile-android"
expect resp.http.Vary == "User-Agent"
} -run
varnishtest "Test varnish-devicedetect"
server s1 {
rxreq
txresp
} -start
# this example shows how to normalize the user agent string seen by the backend.
varnish v1 -vcl+backend {
include "${projectdir}/../devicedetect.vcl";
sub vcl_recv {
call devicedetect;
}
# override the header before it is sent to the backend
sub vcl_backend_fetch { if (bereq.http.X-UA-Device) { set bereq.http.User-Agent = bereq.http.X-UA-Device; } }
# standard Vary handling code from previous examples.
sub vcl_backend_response {
if (bereq.http.X-UA-Device) {
if (!beresp.http.Vary) { # no Vary at all
set beresp.http.Vary = "X-UA-Device";
} elseif (beresp.http.Vary !~ "X-UA-Device") { # add to existing Vary
set beresp.http.Vary = beresp.http.Vary + ", X-UA-Device";
}
}
set beresp.http.X-UA-Device = bereq.http.X-UA-Device;
}
sub vcl_deliver {
if ((req.http.X-UA-Device) && (resp.http.Vary)) {
set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent");
}
}
} -start
client c1 {
txreq -hdr "User-Agent: Mozilla/5.0 (Linux; U; Android 2.2; nb-no; HTC Desire Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
rxresp
expect resp.http.X-UA-Device == "mobile-android"
expect resp.http.Vary == "User-Agent"
} -run
varnishtest "Test varnish-devicedetect"
server s1 {
rxreq
txresp
} -start
varnish v1 -vcl+backend {
include "${projectdir}/../devicedetect.vcl";
sub vcl_recv {
call devicedetect;
}
# do this after vcl_hash, so all Vary-ants can be purged in one go. (avoid ban()ing)
sub vcl_backend_fetch {
if ((bereq.http.X-UA-Device) && (bereq.method == "GET")) {
# if there are existing GET arguments;
if (bereq.url ~ "\?") {
set bereq.http.X-get-devicetype = "&devicetype=" + bereq.http.X-UA-Device;
} else {
set bereq.http.X-get-devicetype = "?devicetype=" + bereq.http.X-UA-Device;
}
set bereq.url = bereq.url + bereq.http.X-get-devicetype;
unset bereq.http.X-get-devicetype;
}
}
# Handle redirects, otherwise standard Vary handling code from previous examples.
sub vcl_backend_response {
if (bereq.http.X-UA-Device) {
if (!beresp.http.Vary) { # no Vary at all
set beresp.http.Vary = "X-UA-Device";
} elseif (beresp.http.Vary !~ "X-UA-Device") { # add to existing Vary
set beresp.http.Vary = beresp.http.Vary + ", X-UA-Device";
}
# if the backend returns a redirect (think missing trailing slash), we
# will potentially show the extra address to the client. we don't want that.
# if the backend reorders the get parameters, you may need to be smarter here. (? and & ordering)
if (beresp.status == 301 || beresp.status == 302 || beresp.status == 303) {
set beresp.http.Location = regsub(beresp.http.location, "[?&]devicetype=.*$", "");
}
}
set beresp.http.X-UA-Device = bereq.http.X-UA-Device;
}
sub vcl_deliver {
if ((req.http.X-UA-Device) && (resp.http.Vary)) {
set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent");
}
}
} -start
client c1 {
txreq -hdr "User-Agent: Mozilla/5.0 (Linux; U; Android 2.2; nb-no; HTC Desire Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
rxresp
expect resp.http.X-UA-Device == "mobile-android"
} -run
......@@ -8,10 +8,14 @@ TESTS=*.vtc
all: controlset $(TESTS)
.PHONY: all controlset $(TESTS)
.PHONY: all controlset snippets $(TESTS)
controlset: ../controlset.txt vtc-from-controlset.py
./vtc-from-controlset.py > 99-controlset.vtc
$(TESTS): controlset
snippets: ../INSTALL.rst vtc-from-snippets.py
rm snippet*vtc
./vtc-from-snippets.py ../INSTALL.rst
$(TESTS): controlset snippets
${VARNISHTEST} -Dvarnishd=${VARNISHD} -Dprojectdir=$(PWD) $@
......@@ -13,7 +13,10 @@ server s1 {
} -start
varnish v1 -vcl+backend {
include "${projectdir}/../devicedetect.vcl";
sub vcl_deliver { class devicedetect; set resp.http.X-UA-Device = req.http.X-UA-Device; }
sub vcl_deliver {
call devicedetect;
set resp.http.X-UA-Device = req.http.X-UA-Device;
}
} -start
client c1 {"""
......
#!/usr/bin/env python
"""
Pick out the examples from the installation documentation and
build a VTC test case around it.
This is to make sure that our documented examples are always runnable.
Format from the input file:
.. foo-start
vcl code here
.. foo-end
foo is a name that describes the vcl snippet.
"""
from sys import argv, stdout
from pprint import pprint
def header(description):
return """varnishtest "%s"
server s1 {
rxreq
txresp
} -start
varnish v1 -vcl+backend {
include "${projectdir}/../devicedetect.vcl";""" % description
def tailer(optional_req):
s = """} -start
client c1 {
"""
if req:
s += " # from rst\n"
s += req
else:
s += """
txreq -req GET -url /foo -hdr "User-Agent: Mozilla/5.0 (Linux; U; Android 2.2; nb-no; HTC Desire Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1" -hdr "Host: example.com"
rxresp
expect resp.status == 200"""
s += """
} -run"""
return s
def parse(inputfile):
section = None
buf = []
req = []
for line in open(inputfile):
if line.startswith(".. req:"):
req.append(line[len(".. req:"):])
continue
if line.startswith(".. startsnippet-"):
section = line.replace(".. startsnippet-", "")[:-1]
continue
if line.startswith(".. endsnippet-"):
try:
assert section is not None
assert line.startswith(".. endsnippet-%s" % section)
except AssertionError:
print section, line
print buf
print req
raise
yield section, "".join(buf), " ".join(req)
section = None
buf = []
req = []
if section:
# No allowed to have comments/"startsnippet" inside block sections.
if line.startswith("VCL::"):
continue
if "include" in line and "devicedetect.vcl" in line:
continue
buf += [line]
if __name__ == "__main__":
rstfile = argv[1]
for name, testsnippet, req in parse(rstfile):
with open("snippet-%s.vtc" % name, "w+") as fp:
print >>fp, header(name)
print >>fp, testsnippet
print >>fp, tailer(req)
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