Remove redirects from cdn to legacy, since canonical domain now has whitelist embedded.  Fixes #214

sub vcl_recv {
#FASTLY recv

	if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
		return(pass);
	}

	if (req.url ~ "^/v1/polyfill\." && req.url !~ "[\?\&]ua=") {
		set req.http.X-Orig-URL = req.url;
		set req.url = "/v1/normalizeUa?ua=" urlencode(req.http.User-Agent);
	}

	return(lookup);
}

sub vcl_deliver {
#FASTLY deliver
	if (req.url ~ "^/v1/normalizeUa" && resp.status == 200) {
		set req.http.Fastly-force-Shield = "1";
		if (req.http.X-Orig-URL ~ "\?") {
			set req.url = req.http.X-Orig-URL "&ua=" resp.http.Normalized-User-Agent;
		} else {
			set req.url = req.http.X-Orig-URL "?ua=" resp.http.Normalized-User-Agent;
		}
		restart;
	} else if (req.url ~ "^/v1/polyfill\..*[\?\&]ua=" && req.http.X-Orig-URL && req.http.X-Orig-URL !~ "[\?\&]ua=") {
		set resp.http.Vary = "Accept-Encoding, User-Agent";
	}
	return(deliver);
}

sub vcl_recv {
#FASTLY recv

	if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
	  return(pass);
	}

	# Redirect legacy service users to legacy service
	set req.http.X-Referer-Host = regsub(req.http.Referer, "^https?\:\/\/([^\/]+)(\/.*)?$", "\1");
	if (req.url == "/normalize.js" ||
		req.url == "/normalize.css" ||
		req.url == "/normalise.js" ||
		req.url == "/normalise.css" ||
		req.http.X-Referer-Host == "www.manrepeller.com" ||
		req.http.X-Referer-Host == "www.mulesoft.com" ||
		req.http.X-Referer-Host == "iluoghidelcuore.it" ||
		req.http.X-Referer-Host == "it.donga.com" ||
		req.http.X-Referer-Host == "www.watchguard.com" ||
		req.http.X-Referer-Host == "www.mulesoft.org" ||
		req.http.X-Referer-Host == "asmwall.com" ||
		req.http.X-Referer-Host == "www.jcsu.edu" ||
		req.http.X-Referer-Host == "adscendmedia.com" ||
		req.http.X-Referer-Host == "game.donga.com" ||
		req.http.X-Referer-Host == "framkino.no" ||
		req.http.X-Referer-Host == "www.accesswireless.com" ||
		req.http.X-Referer-Host == "represent.com" ||
		req.http.X-Referer-Host == "www.designergreek.com" ||
		req.http.X-Referer-Host == "www.plumbingoverstock.com" ||
		req.http.X-Referer-Host == "servicos.searh.rn.gov.br" ||
		req.http.X-Referer-Host == "www.jane-usa.com" ||
		req.http.X-Referer-Host == "www.juntaempresa.com.br" ||
		req.http.X-Referer-Host == "watchguard.com" ||
		req.http.X-Referer-Host == "www.logianalytics.com" ||
		req.http.X-Referer-Host == "www.thebathpoint.com" ||
		req.http.X-Referer-Host == "blogs.mulesoft.org" ||
		req.http.X-Referer-Host == "www.purecanadiangaming.com" ||
		req.http.X-Referer-Host == "play.egzaminatorius.lt" ||
		req.http.X-Referer-Host == "internetacademi.com" ||
		req.http.X-Referer-Host == "www.skyhighnetworks.com" ||
		req.http.X-Referer-Host == "interactive.guim.co.uk" ||
		req.http.X-Referer-Host == "www.soloio.com" ||
		req.http.X-Referer-Host == "desenvolvimentoparaweb.com" ||
		req.http.X-Referer-Host == "daily-fantasy.trunk.development.manhattan.gq1.yahoo.com" ||
		req.http.X-Referer-Host == "blog.damyller.com.br" ||
		req.http.X-Referer-Host == "doggiebag.no" ||
		req.http.X-Referer-Host == "www.beverlydiamonds.com" ||
		req.http.X-Referer-Host == "www.rogerscup.com" ||
		req.http.X-Referer-Host == "blog.hudy.cz" ||
		req.http.X-Referer-Host == "techhamlet.com" ||
		req.http.X-Referer-Host == "www.flirtfachmann.de" ||
		req.http.X-Referer-Host == "alpacacomics.com" ||
		req.http.X-Referer-Host == "areariservata.fondoprevidenzafinanze.it" ||
		req.http.X-Referer-Host == "www.framkino.no" ||
		req.http.X-Referer-Host == "www.nationalsecurities.com" ||
		req.http.X-Referer-Host == "www.tenniscanada.com" ||
		req.http.X-Referer-Host == "libek.org.rs" ||
		req.http.X-Referer-Host == "learn.logianalytics.com" ||
		req.http.X-Referer-Host == "www.impartner.lt" ||
		req.http.X-Referer-Host == "chameleon.biworldwide.com" ||
		req.http.X-Referer-Host == "www.pujckapowebu.cz" ||
		req.http.X-Referer-Host == "www.dlcnetwork.com" ||
		req.http.X-Referer-Host == "www.forbes.com" ||
		req.http.X-Referer-Host == "blog.skyhighnetworks.com" ||
		req.http.X-Referer-Host == "rflx.bjornborg.com" ||
		req.http.X-Referer-Host == "www.coach-outlet-store.com" ||
		req.http.X-Referer-Host == "gist.io" ||
		req.http.X-Referer-Host == "www.suicideispreventable.org" ||
		req.http.X-Referer-Host == "www.rangemastertacticalgear.com" ||
		req.http.X-Referer-Host == "www.hcpozicka.sk" ||
		req.http.X-Referer-Host == "www.airsoftmegastore.com" ||
		req.http.X-Referer-Host == "colortile.github.io" ||
		req.http.X-Referer-Host == "brasstacksmadras.com" ||
		req.http.X-Referer-Host == "hotelcasadomingo.com.mx" ||
		req.http.X-Referer-Host == "www.worcesterpromotions.co.uk" ||
		req.http.X-Referer-Host == "www.asmwall.com" ||
		req.http.X-Referer-Host == "training.mulesoft.com" ||
		req.http.X-Referer-Host == "exhilarateme.com" ||
		req.http.X-Referer-Host == "www.kommentatorskampen.viasatsport.se" ||
		req.http.X-Referer-Host == "www.golfgearselect.com" ||
		req.http.X-Referer-Host == "www.hcpujcka.cz" ||
		req.http.X-Referer-Host == "www.industriallawsociety.org.uk" ||
		req.http.X-Referer-Host == "www.locawebcorp.com.br" ||
		req.http.X-Referer-Host == "giffffr.giphy.com" ||
		req.http.X-Referer-Host == "mccall.co.uk" ||
		req.http.X-Referer-Host == "kommentatorskampen.viasatsport.se" ||
		req.http.X-Referer-Host == "miraclebodyandpaint.com" ||
		req.http.X-Referer-Host == "www.tymprozdravi.cz" ||
		req.http.X-Referer-Host == "delicius.it" ||
		req.http.X-Referer-Host == "www.bluehighwaygames.com" ||
		req.http.X-Referer-Host == "tales-of-pikohan.de" ||
		req.http.X-Referer-Host == "demo.restoreadytest.com" ||
		req.http.X-Referer-Host == "mark.stratmann.me" ||
		req.http.X-Referer-Host == "apixtechnology.com" ||
		req.http.X-Referer-Host == "www.telefonickapozicka.sk" ||
		req.http.X-Referer-Host == "www.telefonnipujcka.cz" ||
		req.http.X-Referer-Host == "www.iteo.no" ||
		req.http.X-Referer-Host == "www.diktate.info" ||
		req.http.X-Referer-Host == "bpharm.192.168.56.102.xip.io" ||
		req.http.X-Referer-Host == "www.artisanprecast.com" ||
		req.http.X-Referer-Host == "tcisolutions.com" ||
		req.http.X-Referer-Host == "icelebrate.icehotel.com" ||
		req.http.X-Referer-Host == "www.thirtysevenwest.com" ||
		req.http.X-Referer-Host == "www.asgllc.com" ||
		req.http.X-Referer-Host == "www.mailinbox.net" ||
		req.http.X-Referer-Host == "rustyfelty.com" ||
		req.http.X-Referer-Host == "pulse.p2c.com" ||
		req.http.X-Referer-Host == "www.framkino.com" ||
		req.http.X-Referer-Host == "armourselfstorage.com" ||
		req.http.X-Referer-Host == "www.dezrezblog.com" ||
		req.http.X-Referer-Host == "goodpeople.com" ||
		req.http.X-Referer-Host == "www.eden-houlgate.com" ||
		req.http.X-Referer-Host == "www.patiovirtual.com.br" ||
		req.http.X-Referer-Host == "stats.egzaminatorius.lt" ||
		req.http.X-Referer-Host == "www.movieville.org" ||
		req.http.X-Referer-Host == "secure.watchguard.com" ||
		req.http.X-Referer-Host == "nationalsecurities.com" ||
		req.http.X-Referer-Host == "www.snaponindustrial.eu" ||
		req.http.X-Referer-Host == "www.fangear.com" ||
		req.http.X-Referer-Host == "www.scribbleshop.com" ||
		req.http.X-Referer-Host == "forums.mulesoft.org" ||
		req.http.X-Referer-Host == "dataentries.in" ||
		req.http.X-Referer-Host == "maki.alconost.com" ||
		req.http.X-Referer-Host == "dixso.github.io" ||
		req.http.X-Referer-Host == "citaprevia.volkswagen.es" ||
		req.http.X-Referer-Host == "movieville.org" ||
		req.http.X-Referer-Host == "www.seoestudios.com" ||
		req.http.X-Referer-Host == "tickets.campo.nu" ||
		req.http.X-Referer-Host == "perriehale.toucan" ||
		req.http.X-Referer-Host == "online.printstation.cz" ||
		req.http.X-Referer-Host == "www.le-bistrot-basque.com" ||
		req.http.X-Referer-Host == "www.thaiartofmassage.com" ||
		req.http.X-Referer-Host == "www.seevolution.com" ||
		req.http.X-Referer-Host == "janeusa.david" ||
		req.http.X-Referer-Host == "www.goodpeople.com" ||
		req.http.X-Referer-Host == "feeds.feedburner.com" ||
		req.http.X-Referer-Host == "www.millwardesque.com" ||
		req.http.X-Referer-Host == "cci.smartucs.a" ||
		req.http.X-Referer-Host == "forum.tales-of-pikohan.de" ||
		req.http.X-Referer-Host == "small.mu" ||
		req.http.X-Referer-Host == "crisp.toucan" ||
		req.http.X-Referer-Host == "dev1776.nationalsecurities.com" ||
		req.http.X-Referer-Host == "byose.net" ||
		req.http.X-Referer-Host == "www.eurodiary.ie" ||
		req.http.X-Referer-Host == "lemouvementassociatif.org" ||
		req.http.X-Referer-Host == "www.ey.com" ||
		req.http.X-Referer-Host == "cuterunslive.com" ||
		req.http.X-Referer-Host == "blog.odynia.org" ||
		req.http.X-Referer-Host == "solarenaymar.com.ar" ||
		req.http.X-Referer-Host == "www.aispaitalia.it" ||
		req.http.X-Referer-Host == "www.startupsaturday.it" ||
		req.http.X-Referer-Host == "support.watchguard.com" ||
		req.http.X-Referer-Host == "dax.absolnet.se" ||
		req.http.X-Referer-Host == "www.creativeinnovation.net.au" ||
		req.http.X-Referer-Host == "dev.raird.no" ||
		req.http.X-Referer-Host == "lesjeteursdencre.fr" ||
		req.http.X-Referer-Host == "itnotes.pl" ||
		req.http.X-Referer-Host == "www.thesocietyinternational.com" ||
		req.http.X-Referer-Host == "terminalfx.com" ||
		req.http.X-Referer-Host == "draft.sx" ||
		req.http.X-Referer-Host == "atelier-florent.fr" ||
		req.http.X-Referer-Host == "www.buona-tavola-ristorante.fr" ||
		req.http.X-Referer-Host == "www.terminalfx.com" ||
		req.http.X-Referer-Host == "www.taxattorney.com" ||
		req.http.X-Referer-Host == "proenter.dev" ||
		req.http.X-Referer-Host == "bodelo-advocaten.be" ||
		req.http.X-Referer-Host == "www.football1asia.com" ||
		req.http.X-Referer-Host == "rets.ly" ||
		req.http.X-Referer-Host == "erriol.com" ||
		req.http.X-Referer-Host == "shssandiego.com" ||
		req.http.X-Referer-Host == "notecrow.appspot.com" ||
		req.http.X-Referer-Host == "framkino.com" ||
		req.http.X-Referer-Host == "sexsite.daan" ||
		req.http.X-Referer-Host == "sro.dk" ||
		req.http.X-Referer-Host == "www.coloft.com" ||
		req.http.X-Referer-Host == "taxiplus.vsystem.hu" ||
		req.http.X-Referer-Host == "stage.mulesoft.com" ||
		req.http.X-Referer-Host == "local.dev.ugameplan.com" ||
		req.http.X-Referer-Host == "posh-nosh.com" ||
		req.http.X-Referer-Host == "phongthuyshop.vn" ||
		req.http.X-Referer-Host == "local.mansionhouse.co.uk" ||
		req.http.X-Referer-Host == "www.compareaccounting.com" ||
		req.http.X-Referer-Host == "news.coinometrics.com" ||
		req.http.X-Referer-Host == "prod-yann.mailinbox.net" ||
		req.http.X-Referer-Host == "rachelree.se" ||
		req.http.X-Referer-Host == "dev1.accesswireless.usdigitalpartners.net" ||
		req.http.X-Referer-Host == "www.ewirisk.com" ||
		req.http.X-Referer-Host == "snapchan.com" ||
		req.http.X-Referer-Host == "www.trikatasieraakademija.lv" ||
		req.http.X-Referer-Host == "www.fosteringadoptionswindon.org.uk" ||
		req.http.X-Referer-Host == "patiovirtual.com.br" ||
		req.http.X-Referer-Host == "www.libmanpro.com" ||
		req.http.X-Referer-Host == "www.diktate.dev" ||
		req.http.X-Referer-Host == "idisco.pianetaitalia.intra" ||
		req.http.X-Referer-Host == "strudelfolhadinho.com.br" ||
		req.http.X-Referer-Host == "www.cunesoft.com" ||
		req.http.X-Referer-Host == "www.shropshirefoundation.org" ||
		req.http.X-Referer-Host == "speakingofbeauty.tv" ||
		req.http.X-Referer-Host == "www.sbs.com.au" ||
		req.http.X-Referer-Host == "bosch.pikmahosting.de" ||
		req.http.X-Referer-Host == "janeusa.lol" ||
		req.http.X-Referer-Host == "eso.toucan" ||
		req.http.X-Referer-Host == "www.lamedicis.com" ||
		req.http.X-Referer-Host == "cereseurope.com" ||
		req.http.X-Referer-Host == "www.pizza-moltobene.fr" ||
		req.http.X-Referer-Host == "www.kaptiva.com.br" ||
		req.http.X-Referer-Host == "flixified.com" ||
		req.http.X-Referer-Host == "www.12roundsboxing.co.uk" ||
		req.http.X-Referer-Host == "www.tensionautomation.com" ||
		req.http.X-Referer-Host == "www.copyprint.nl" ||
		req.http.X-Referer-Host == "unisol" ||
		req.http.X-Referer-Host == "timepad.ews.co.rs" ||
		req.http.X-Referer-Host == "dev.rets.ly" ||
		req.http.X-Referer-Host == "www.vrhabilis.com" ||
		req.http.X-Referer-Host == "trondheggelund.no" ||
		req.http.X-Referer-Host == "www.ferienwohnungen-lebenswert.de" ||
		req.http.X-Referer-Host == "blog.mainline.com" ||
		req.http.X-Referer-Host == "arcstonepartners.com" ||
		req.http.X-Referer-Host == "dev-mj" ||
		req.http.X-Referer-Host == "www.crisp-cpd.com" ||
		req.http.X-Referer-Host == "purecanadiangaming.com" ||
		req.http.X-Referer-Host == "mainline.com" ||
		req.http.X-Referer-Host == "idx.dev.rets.ly" ||
		req.http.X-Referer-Host == "eneco.flicker.visualwind.com" ||
		req.http.X-Referer-Host == "mi.etoilemedia.it" ||
		req.http.X-Referer-Host == "www.atmosphericwatergenerator.net" ||
		req.http.X-Referer-Host == "www.gasnaturalenergyclass.com" ||
		req.http.X-Referer-Host == "salesmarketing.championpetfoods.com" ||
		req.http.X-Referer-Host == "www.watchgaurd.com" ||
		req.http.X-Referer-Host == "vm-sqlext-svil" ||
		req.http.X-Referer-Host == "dev.accesswireless.usdigitalpartners.net" ||
		req.http.X-Referer-Host == "kommentatorskampen.ohmytest.se" ||
		req.http.X-Referer-Host == "m.euvivoesporte.com.br" ||
		req.http.X-Referer-Host == "blog.dasroot.net" ||
		req.http.X-Referer-Host == "bugsdashboard.com" ||
		req.http.X-Referer-Host == "www.eurodiary.co.uk" ||
		req.http.X-Referer-Host == "seecanadabetter-app.appspot.com" ||
		req.http.X-Referer-Host == "www.bobbychanblog.com" ||
		req.http.X-Referer-Host == "www.mybenefitsatwork.co.uk" ||
		req.http.X-Referer-Host == "www.fotolotos.lt" ||
		req.http.X-Referer-Host == "robferguson.org" ||
		req.http.X-Referer-Host == "staging.motherapp.com" ||
		req.http.X-Referer-Host == "www.undergroundminingequipmentcompaniescalgaryquotes.com" ||
		req.http.X-Referer-Host == "idisco.pianetaitalia.com" ||
		req.http.X-Referer-Host == "cliocalman.com" ||
		req.http.X-Referer-Host == "sexymirror-app.com" ||
		req.http.X-Referer-Host == "taxes-in-serbia.192.168.56.102.xip.io" ||
		req.http.X-Referer-Host == "wunderbiz.com" ||
		req.http.X-Referer-Host == "thebathpoint.webdemo.es" ||
		req.http.X-Referer-Host == "www.restaurant-lemenhir.com" ||
		req.http.X-Referer-Host == "www.valdeure.fr" ||
		req.http.X-Referer-Host == "bpharm.ews.co.rs" ||
		req.http.X-Referer-Host == "teachersdunet.com" ||
		req.http.X-Referer-Host == "livesmartswmo.com" ||
		req.http.X-Referer-Host == "javiblog.com" ||
		req.http.X-Referer-Host == "mi.etoilemedia.com" ||
		req.http.X-Referer-Host == "www.lens.org" ||
		req.http.X-Referer-Host == "blacknet.co.uk" ||
		req.http.X-Referer-Host == "my-faith-be-like.ghost.io" ||
		req.http.X-Referer-Host == "laravel.dev" ||
		req.http.X-Referer-Host == "fi-hpotts.2014-mansionhouse" ||
		req.http.X-Referer-Host == "mi.mailinbox.net" ||
		req.http.X-Referer-Host == "www.schulkeplusrewards.co.uk" ||
		req.http.X-Referer-Host == "sasha-project.s3.amazonaws.com" ||
		req.http.X-Referer-Host == "www.solarenaymar.com.ar" ||
		req.http.X-Referer-Host == "generate.visualwind.com" ||
		req.http.X-Referer-Host == "drupaltest.wgti.net" ||
		req.http.X-Referer-Host == "minasdev.org" ||
		req.http.X-Referer-Host == "ims-logistic.dev" ||
		req.http.X-Referer-Host == "viaresto.com" ||
		req.http.X-Referer-Host == "learn.logixml.com" ||
		req.http.X-Referer-Host == "ox.espadrill-admin" ||
		req.http.X-Referer-Host == "smallmu-sandbox.s3-ap-southeast-1.amazonaws.com" ||
		req.http.X-Referer-Host == "www.firstinternet.co.uk" ||
		req.http.X-Referer-Host == "m.facebook.com" ||
		req.http.X-Referer-Host == "www.ace.media" ||
		req.http.X-Referer-Host == "www.ewiretx.com" ||
		req.http.X-Referer-Host == "crisp.test.tdev.co.uk" ||
		req.http.X-Referer-Host == "www.mifabricavisual.com" ||
		req.http.X-Referer-Host == "www.targetintervention.no" ||
		req.http.X-Referer-Host == "www.couperogers.com" ||
		req.http.X-Referer-Host == "skyhighstaging.local.com" ||
		req.http.X-Referer-Host == "altorendimientoacademico.queretaro.itesm.mx" ||
		req.http.X-Referer-Host == "www.gall-holl.com" ||
		req.http.X-Referer-Host == "sbs-moad.s3-ap-southeast-2.amazonaws.com" ||
		req.http.X-Referer-Host == "firstint.recruitment-lite" ||
		req.http.X-Referer-Host == "www.sro.dk" ||
		req.http.X-Referer-Host == "www.menbur.com" ||
		req.http.X-Referer-Host == "libmanpro.com" ||
		req.http.X-Referer-Host == "blurayaustralia.com" ||
		req.http.X-Referer-Host == "www.adscendmedia.com" ||
		req.http.X-Referer-Host == "blog.vincentbrouillet.com" ||
		req.http.X-Referer-Host == "www.investiumonline.nl" ||
		req.http.X-Referer-Host == "burakkp.com" ||
		req.http.X-Referer-Host == "audreysystem.seeties.me" ||
		req.http.X-Referer-Host == "everestsalesconsultants.co.uk" ||
		req.http.X-Referer-Host == "accesswireless.usdphosting.com" ||
		req.http.X-Referer-Host == "frota.searh.rn.gov.br" ||
		req.http.X-Referer-Host == "qa.ugameplan.com" ||
		req.http.X-Referer-Host == "secure.yec.co" ||
		req.http.X-Referer-Host == "connect.eu.mulesoft.com" ||
		req.http.X-Referer-Host == "www.lastlongerreview.com" ||
		req.http.X-Referer-Host == "kommentatorskampen.lo" ||
		req.http.X-Referer-Host == "kido.bigroominternet.co.uk" ||
		req.http.X-Referer-Host == "ohmydev.se" ||
		req.http.X-Referer-Host == "www.bentogarcia.es" ||
		req.http.X-Referer-Host == "rearview.herokai.com" ||
		req.http.X-Referer-Host == "lecafelatin.resto-ready.com" ||
		req.http.X-Referer-Host == "html24-dev.dk" ||
		req.http.X-Referer-Host == "seecanadabetter.ddev" ||
		req.http.X-Referer-Host == "portal.ad1.wrvc.co.uk" ||
		req.http.X-Referer-Host == "garthwunsch.com" ||
		req.http.X-Referer-Host == "www.elitecustomsigns.com" ||
		req.http.X-Referer-Host == "kaptiva.com.br" ||
		req.http.X-Referer-Host == "nordicinox.com" ||
		req.http.X-Referer-Host == "www.nataktivisterna.lo" ||
		req.http.X-Referer-Host == "www.fidalgobaycoffee.com" ||
		req.http.X-Referer-Host == "mailinbox.net" ||
		req.http.X-Referer-Host == "www.tension.com" ||
		req.http.X-Referer-Host == "www.mccall.co.uk" ||
		req.http.X-Referer-Host == "petbucks.org" ||
		req.http.X-Referer-Host == "mulesoftcompdev.prod.acquia-sites.com" ||
		req.http.X-Referer-Host == "logidev.devcloud.acquia-sites.com" ||
		req.http.X-Referer-Host == "www.cuterunslive.com" ||
		req.http.X-Referer-Host == "ec2-54-86-27-137.compute-1.amazonaws.com" ||
		req.http.X-Referer-Host == "coloft.com" ||
		req.http.X-Referer-Host == "dev-yann.mailinbox.net" ||
		req.http.X-Referer-Host == "titistore.es" ||
		req.http.X-Referer-Host == "biztro.pl" ||
		req.http.X-Referer-Host == "zd.ews.co.rs" ||
		req.http.X-Referer-Host == "www.cafelatincaen.com" ||
		req.http.X-Referer-Host == "togume.com" ||
		req.http.X-Referer-Host == "connect.mulesoft.com" ||
		req.http.X-Referer-Host == "nataktivisterna.lo" ||
		req.http.X-Referer-Host == "livelean.co.uk" ||
		req.http.X-Referer-Host == "copyprint.nl" ||
		req.http.X-Referer-Host == "jayhathaway.com" ||
		req.http.X-Referer-Host == "rugsejis.epratybos.lt" ||
		req.http.X-Referer-Host == "media-env.elasticbeanstalk.com" ||
		req.http.X-Referer-Host == "id.snowfire.io" ||
		req.http.X-Referer-Host == "employme.at" ||
		req.http.X-Referer-Host == "booleanbrothers.com" ||
		req.http.X-Referer-Host == "www.fusiongroup.ie" ||
		req.http.X-Referer-Host == "skyhighnetworks.gravitatestaging.com" ||
		req.http.X-Referer-Host == "www.teachers.youtalkonline.com" ||
		req.http.X-Referer-Host == "sunshineandfrank.com" ||
		req.http.X-Referer-Host == "proof.acemedia.bigroominternet.co.uk" ||
		req.http.X-Referer-Host == "personaltrainersinlosangeles.com" ||
		req.http.X-Referer-Host == "www.shawn.co" ||
		req.http.X-Referer-Host == "solidproperty.com.my" ||
		req.http.X-Referer-Host == "www.nordicinox.com" ||
		req.http.X-Referer-Host == "tension.com" ||
		req.http.X-Referer-Host == "especiaiss3.gshow.globo.com" ||
		req.http.X-Referer-Host == "seebstore.com" ||
		req.http.X-Referer-Host == "packerz.uk" ||
		req.http.X-Referer-Host == "happyliving.jp" ||
		req.http.X-Referer-Host == "www.vacacionesdebuenrollito.com" ||
		req.http.X-Referer-Host == "plastpack.ind.br" ||
		req.http.X-Referer-Host == "finance.trunk.development.manhattan.gq1.yahoo.com" ||
		req.http.X-Referer-Host == "elearningpprd.biworldwide.com" ||
		req.http.X-Referer-Host == "millwardesque.com" ||
		req.http.X-Referer-Host == "icehotel.lo" ||
		req.http.X-Referer-Host == "ongair.im" ||
		req.http.X-Referer-Host == "mule01a.managed.contegix.com" ||
		req.http.X-Referer-Host == "ipated.org" ||
		req.http.X-Referer-Host == "cientifica-research.com" ||
		req.http.X-Referer-Host == "andreypopp.github.io" ||
		req.http.X-Referer-Host == "schools.medsense.in" ||
		req.http.X-Referer-Host == "d-ycl.cigna.com" ||
		req.http.X-Referer-Host == "romankorver.com" ||
		req.http.X-Referer-Host == "ihaveanidea.me" ||
		req.http.X-Referer-Host == "evoyan.org" ||
		req.http.X-Referer-Host == "cectserver.dyndns.org" ||
		req.http.X-Referer-Host == "test.idx.dev.rets.ly" ||
		req.http.X-Referer-Host == "vastgoedpro.dataquote.nl" ||
		req.http.X-Referer-Host == "sifopweb" ||
		req.http.X-Referer-Host == "instituteofselfmastery.com.au" ||
		req.http.X-Referer-Host == "www.automobrella.com" ||
		req.http.X-Referer-Host == "panel.mibubu.com" ||
		req.http.X-Referer-Host == "whsb.herokuapp.com" ||
		req.http.X-Referer-Host == "heastea.com" ||
		req.http.X-Referer-Host == "meishinkan.pl" ||
		req.http.X-Referer-Host == "manrepeller.staging.wpengine.com" ||
		req.http.X-Referer-Host == "derprecated.com" ||
		req.http.X-Referer-Host == "fobesfamily.com" ||
		req.http.X-Referer-Host == "tensionautomation.com" ||
		req.http.X-Referer-Host == "www.jesuisoriginale.com" ||
		req.http.X-Referer-Host == "www.domgonsa.com.ar" ||
		req.http.X-Referer-Host == "www.digitalpark.co.uk" ||
		req.http.X-Referer-Host == "debray.jerome.free.fr" ||
		req.http.X-Referer-Host == "rainmakerthinking.training" ||
		req.http.X-Referer-Host == "thespineauthority.com.au" ||
		req.http.X-Referer-Host == "pseudocubic.com" ||
		req.http.X-Referer-Host == "mi.sky8.it" ||
		req.http.X-Referer-Host == "iteo.staging.wpengine.com" ||
		req.http.X-Referer-Host == "adscend-q.dev" ||
		req.http.X-Referer-Host == "taxes-in-serbia.ews.co.rs" ||
		req.http.X-Referer-Host == "s.codepen.io" ||
		req.http.X-Referer-Host == "seecanadabetter.ca" ||
		req.http.X-Referer-Host == "www.werkbund.com.br" ||
		req.http.X-Referer-Host == "www.art365.co.za" ||
		req.http.X-Referer-Host == "tensionpackaging.com" ||
		req.http.X-Referer-Host == "ec2-54-164-179-111.compute-1.amazonaws.com" ||
		req.http.X-Referer-Host == "sms.loc" ||
		req.http.X-Referer-Host == "timeclock.neonandmore.com" ||
		req.http.X-Referer-Host == "silverblingblack.com" ||
		req.http.X-Referer-Host == "www.modulolanguage.com" ||
		req.http.X-Referer-Host == "visualise.today" ||
		req.http.X-Referer-Host == "pickinglist.constance.com.br" ||
		req.http.X-Referer-Host == "mobileshoppingapps.net" ||
		req.http.X-Referer-Host == "ashleyandbarry.com" ||
		req.http.X-Referer-Host == "www.planetgreen.cl" ||
		req.http.X-Referer-Host == "portal-intranet.ti.sabesp.com.br" ||
		req.http.X-Referer-Host == "gordonrecords.net" ||
		req.http.X-Referer-Host == "cce.pathlore.net" ||
		req.http.X-Referer-Host == "www.myclaimvalue.com" ||
		req.http.X-Referer-Host == "stage.mulesoft.org" ||
		req.http.X-Referer-Host == "javiblog.javigs.es"
	) {
		set req.http.X-Redir-URL = "https://legacy.polyfill.io" req.url;
		error 900 req.http.X-Redir-URL;
	}

	if (req.url ~ "^/v1/polyfill\." && req.url !~ "[\?\&]ua=") {
		set req.http.X-Orig-URL = req.url;
		set req.url = "/v1/normalizeUa?ua=" regsuball(regsuball(regsuball(req.http.User-Agent, {"%"}, {"%25"}), {" "}, {"%20"}), {"&"}, {"%26"});
	}

	return(lookup);
}

sub vcl_deliver {
#FASTLY deliver
	if (req.url ~ "^/v1/normalizeUa" && resp.status == 200) {
		set req.http.Fastly-force-Shield = "1";
		if (req.http.X-Orig-URL ~ "\?") {
			set req.url = req.http.X-Orig-URL "&ua=" resp.http.Normalized-User-Agent;
		} else {
			set req.url = req.http.X-Orig-URL "?ua=" resp.http.Normalized-User-Agent;
		}
		restart;
	} else if (req.url ~ "^/v1/polyfill\..*[\?\&]ua=" && req.http.X-Orig-URL && req.http.X-Orig-URL !~ "[\?\&]ua=") {
		set resp.http.Vary = "Accept-Encoding, User-Agent";
	}
	return(deliver);
}

sub vcl_error {
	if (obj.status == 900) {
		set obj.http.Location = obj.response;
		set obj.status = 301;
		set obj.response = "Moved Permanently";
		synthetic {"The referer header or request URI supplied in your request is known to be an attempt to use the previous version of polyfill.io, so for backwards compatibility we're redirecting you to legacy.polyfill.io which will handle your request using the old version of the service.  The legacy version will be discontinued in the future, so please update to the new version, and then let us know by raising an issue in our repo at https://github.com/financial-times/polyfill-service."};
		return (deliver);
	}
}


set the correct header

set the correct header

#
# detectdevice.vcl - regex based device detection for Varnish
# http://github.com/lkarsten/varnish-devicedetect/
#
# Author: Lasse Karstensen <lasse@varnish-software.com>

sub devicedetect {
    unset req.http.X-hash-input;
    unset req.http.X-UA-Device;
    set req.http.X-UA-Device = "pc";

    if    (req.http.User-Agent ~ "(?i)ip(hone|od)") { set req.http.X-UA-Device = "mobile-iphone"; }
    elsif (req.http.User-Agent ~ "(?i)ipad")        { set req.http.X-UA-Device = "tablet-ipad"; }
    # how do we differ between an android phone and an android tablet?
    # http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent
    elsif (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") { set req.http.X-UA-Device = "mobile-android"; }
    # may very well give false positives towards android tablets. Suggestions welcome.
    elsif (req.http.User-Agent ~ "(?i)android")         { set req.http.X-UA-Device = "tablet-android"; }

    elsif (req.http.User-Agent ~ "^HTC" ||
        req.http.User-Agent ~ "Fennec" ||
        req.http.User-Agent ~ "IEMobile" ||
        req.http.User-Agent ~ "BlackBerry" ||
        req.http.User-Agent ~ "SymbianOS.*AppleWebKit" ||
        req.http.User-Agent ~ "Opera Mobi") {
        set req.http.X-UA-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)symbian" ||
        req.http.User-Agent ~ "(?i)^sonyericsson" ||
        req.http.User-Agent ~ "(?i)^nokia" ||
        req.http.User-Agent ~ "(?i)^samsung" ||
        req.http.User-Agent ~ "(?i)^lg" ||
	req.http.User-Agent ~ "(?i)bada" ||
	req.http.User-Agent ~ "(?i)blazer" ||
	req.http.User-Agent ~ "(?i)cellphone" ||
	req.http.User-Agent ~ "(?i)iemobile" ||
	req.http.User-Agent ~ "(?i)midp-2.0" ||
	req.http.User-Agent ~ "(?i)u990" ||
	req.http.User-Agent ~ "(?i)netfront" ||
	req.http.User-Agent ~ "(?i)opera mini" ||
	req.http.User-Agent ~ "(?i)palm" ||
	req.http.User-Agent ~ "(?i)nintendo wii" ||
	req.http.User-Agent ~ "(?i)playstation portable" ||
	req.http.User-Agent ~ "(?i)portalmmm" ||
	req.http.User-Agent ~ "(?i)proxinet" ||
	req.http.User-Agent ~ "(?i)sonyericsson" ||
	req.http.User-Agent ~ "(?i)symbian" ||
	req.http.User-Agent ~ "(?i)windows\ ?ce" ||
	req.http.User-Agent ~ "(?i)winwap" ||
	req.http.User-Agent ~ "(?i)eudoraweb" ||
	req.http.User-Agent ~ "(?i)htc" ||
	req.http.User-Agent ~ "(?i)240x320" ||
	req.http.User-Agent ~ "(?i)avantgo") {
        set req.http.X-UA-Device = "mobile-generic";
    }

    # handle overrides
    if (req.http.Cookie ~ "(i?)X-UA-Device-force") {
        # ;?? means zero or one ;, non-greedy to match the first.
        set req.http.X-UA-Device = regsub(req.http.Cookie, "(?i).*X-UA-Device-force=([^;]+);??.*", "\1");
    }
    set req.http.X-hash-input = req.http.X-UA-Device;
}

# vim: sw=4:tw=120 # meh

#
# detectdevice.vcl - regex based device detection for Varnish
# http://github.com/lkarsten/varnish-devicedetect/
#
# Author: Lasse Karstensen <lasse@varnish-software.com>

sub devicedetect {
    unset req.http.X-hash-input;
    unset req.http.X-UA-Device;
    set req.http.X-UA-Device = "pc";

    if    (req.http.User-Agent ~ "(?i)ip(hone|od)") { set req.http.X-UA-Device = "mobile-iphone"; }
    elsif (req.http.User-Agent ~ "(?i)ipad")        { set req.http.X-UA-Device = "tablet-ipad"; }
    # how do we differ between an android phone and an android tablet?
    # http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent
    elsif (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") { set req.http.X-UA-Device = "mobile-android"; }
    # may very well give false positives towards android tablets. Suggestions welcome.
    elsif (req.http.User-Agent ~ "(?i)android")         { set req.http.X-UA-Device = "tablet-android"; }

    elsif (req.http.User-Agent ~ "^HTC" ||
        req.http.User-Agent ~ "Fennec" ||
        req.http.User-Agent ~ "IEMobile" ||
        req.http.User-Agent ~ "BlackBerry" ||
        req.http.User-Agent ~ "SymbianOS.*AppleWebKit" ||
        req.http.User-Agent ~ "Opera Mobi") {
        set req.http.X-VG-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)symbian" ||
        req.http.User-Agent ~ "(?i)^sonyericsson" ||
        req.http.User-Agent ~ "(?i)^nokia" ||
        req.http.User-Agent ~ "(?i)^samsung" ||
        req.http.User-Agent ~ "(?i)^lg" ||
	req.http.User-Agent ~ "(?i)bada" ||
	req.http.User-Agent ~ "(?i)blazer" ||
	req.http.User-Agent ~ "(?i)cellphone" ||
	req.http.User-Agent ~ "(?i)iemobile" ||
	req.http.User-Agent ~ "(?i)midp-2.0" ||
	req.http.User-Agent ~ "(?i)u990" ||
	req.http.User-Agent ~ "(?i)netfront" ||
	req.http.User-Agent ~ "(?i)opera mini" ||
	req.http.User-Agent ~ "(?i)palm" ||
	req.http.User-Agent ~ "(?i)nintendo wii" ||
	req.http.User-Agent ~ "(?i)playstation portable" ||
	req.http.User-Agent ~ "(?i)portalmmm" ||
	req.http.User-Agent ~ "(?i)proxinet" ||
	req.http.User-Agent ~ "(?i)sonyericsson" ||
	req.http.User-Agent ~ "(?i)symbian" ||
	req.http.User-Agent ~ "(?i)windows\ ?ce" ||
	req.http.User-Agent ~ "(?i)winwap" ||
	req.http.User-Agent ~ "(?i)eudoraweb" ||
	req.http.User-Agent ~ "(?i)htc" ||
	req.http.User-Agent ~ "(?i)240x320" ||
	req.http.User-Agent ~ "(?i)avantgo") {
        set req.http.X-UA-Device = "mobile-generic";
    }

    # handle overrides
    if (req.http.Cookie ~ "(i?)X-UA-Device-force") {
        # ;?? means zero or one ;, non-greedy to match the first.
        set req.http.X-UA-Device = regsub(req.http.Cookie, "(?i).*X-UA-Device-force=([^;]+);??.*", "\1");
    }
    set req.http.X-hash-input = req.http.X-UA-Device;
}

# vim: sw=4:tw=120 # meh


This file was named wrong, and the test does not make sense anymore, since the backend health information is in the fetch thread

varnishtest "req.backend.healthy in vcl_deliver"

server s1 {
	rxreq
	txresp
} -start

varnish v1 -vcl+backend {

	import ${vmod_std};

	sub vcl_deliver {
	    set resp.http.x-foo = std.healthy(req.backend);
	}
} -start

client c1 {
	txreq
	rxresp
	expect resp.status == 200
} -run


Switch geo location to the new name space per https://docs.fastly.com/guides/migrations/migrating-geolocation-variables-to-the-new-dataset

    # Fixup for Varnish ESI not dealing with https:// absolute URLs well
    if (req.is_esi_subreq && req.url ~ "/https://([^/]+)(/.*)$") {
        set req.http.Host = re.group.1;
        set req.url = re.group.2;
    }

    # Sort the query arguments
    set req.url = boltsort.sort(req.url);

    # bypass language switcher
    if (req.url ~ "(?i)___from_store=.*&___store=.*") {
        set req.http.X-Pass = "1";
    }

    # set HTTPS header for offloaded TLS
    if (req.http.Fastly-SSL) {
        set req.http.Https = "on";
    }

    if (req.http.cookie:FASTLY_CDN_ENV) {
        set req.http.Fastly-Cdn-Env = req.http.cookie:FASTLY_CDN_ENV;
    } else {
        unset req.http.Fastly-Cdn-Env;
    }

    # auth for purging
    if (req.request == "FASTLYPURGE") {

        if (!req.http.X-Purge-Token ~ "^([^_]+)_(.*)" ) {
            error 403;
        }

        declare local var.X-Exp STRING;
        declare local var.X-Sig STRING;
        /* extract token expiration and signature */
        set var.X-Exp = re.group.1;
        set var.X-Sig = re.group.2;

        /* validate signature */
        if (var.X-Sig == regsub(digest.hmac_sha1(req.service_id, req.url.path var.X-Exp), "^0x", "")) {
            /* check that expiration time has not elapsed */
            if (time.is_after(now, std.integer2time(std.atoi(var.X-Exp)))) {
                error 410;
            }

        } else {
            error 403;
        }

    }

    # disable ESI processing on Origin Shield
    if (req.http.Fastly-FF) {
        set req.esi = false;
        # Needed for proper handling of stale while revalidated when shielding is involved
        set req.max_stale_while_revalidate = 0s;
    }

    # static files are always cacheable. remove SSL flag and cookie
    if (req.url ~ "^/(media|js|skin)/.*\.(png|jpg|jpeg|gif|css|js|swf|ico)$") {
        unset req.http.Https;
        unset req.http.Cookie;
    }

    # formkey lookup
    if (req.url ~ "/fastlycdn/getformkey/") {
        # check if we have a formkey cookie
        if (req.http.Cookie:FASTLY_CDN_FORMKEY) {
            set req.http.Formkey = req.http.Cookie:FASTLY_CDN_FORMKEY;
        } else {
            # create formkey
            set req.http.seed = req.http.Cookie client.ip remote.port client.geo.longitude client.geo.latitude client.geo.postal_code;
            set req.http.Formkey = regsub(digest.hash_md5(req.http.seed), "^0x", "");
        }
        error 760 req.http.Formkey;
    }

    # client.geo lookup
    if (req.url ~ "fastlycdn/esi/getcountry/") {
        # check if GeoIP has been already processed by client
        if (req.http.Cookie:FASTLY_CDN_GEOIP_PROCESSED) {
            error 200 "";
        } else {
            # modify req.url and restart request processing
            error 750 client.geo.country_code;
        }
    }

    # client.geo get country code
    if (req.url ~ "fastlycdn/esi/getcountrycode/") {
        # create and set req.http.X-Country-Code
        error 755 client.geo.country_code;
    }

    # check for ESI calls
    if (req.url ~ "esi_data=") {
        # check for valid cookie data
        if (req.http.Cookie ~ "FASTLY_CDN-([A-Za-z0-9-_]+)=([^;]*)") {
            set req.url = querystring.filter(req.url, "esi_data") + "&esi_data=" + re.group.2;
        }
    }

    # Per suggestions in https://github.com/sdinteractive/SomethingDigital_PageCacheParams
    # we'll strip out query parameters used in Google AdWords, Mailchimp tracking
    set req.http.Magento-Original-URL = req.url;
    set req.url = querystring.regfilter(req.url, "^(utm_.*|gclid|gdftrk|_ga|mc_.*)");

    # If object has been marked as pass pass it
    if ( req.http.X-Pass ) {
        return(pass);
    }

    # Fixup for Varnish ESI not dealing with https:// absolute URLs well
    if (req.is_esi_subreq && req.url ~ "/https://([^/]+)(/.*)$") {
        set req.http.Host = re.group.1;
        set req.url = re.group.2;
    }

    # Sort the query arguments
    set req.url = boltsort.sort(req.url);

    # bypass language switcher
    if (req.url ~ "(?i)___from_store=.*&___store=.*") {
        set req.http.X-Pass = "1";
    }

    # set HTTPS header for offloaded TLS
    if (req.http.Fastly-SSL) {
        set req.http.Https = "on";
    }

    if (req.http.cookie:FASTLY_CDN_ENV) {
        set req.http.Fastly-Cdn-Env = req.http.cookie:FASTLY_CDN_ENV;
    } else {
        unset req.http.Fastly-Cdn-Env;
    }

    # auth for purging
    if (req.request == "FASTLYPURGE") {

        if (!req.http.X-Purge-Token ~ "^([^_]+)_(.*)" ) {
            error 403;
        }

        declare local var.X-Exp STRING;
        declare local var.X-Sig STRING;
        /* extract token expiration and signature */
        set var.X-Exp = re.group.1;
        set var.X-Sig = re.group.2;

        /* validate signature */
        if (var.X-Sig == regsub(digest.hmac_sha1(req.service_id, req.url.path var.X-Exp), "^0x", "")) {
            /* check that expiration time has not elapsed */
            if (time.is_after(now, std.integer2time(std.atoi(var.X-Exp)))) {
                error 410;
            }

        } else {
            error 403;
        }

    }

    # disable ESI processing on Origin Shield
    if (req.http.Fastly-FF) {
        set req.esi = false;
        # Needed for proper handling of stale while revalidated when shielding is involved
        set req.max_stale_while_revalidate = 0s;
    }

    # static files are always cacheable. remove SSL flag and cookie
    if (req.url ~ "^/(media|js|skin)/.*\.(png|jpg|jpeg|gif|css|js|swf|ico)$") {
        unset req.http.Https;
        unset req.http.Cookie;
    }

    # formkey lookup
    if (req.url ~ "/fastlycdn/getformkey/") {
        # check if we have a formkey cookie
        if (req.http.Cookie:FASTLY_CDN_FORMKEY) {
            set req.http.Formkey = req.http.Cookie:FASTLY_CDN_FORMKEY;
        } else {
            # create formkey
            set req.http.seed = req.http.Cookie client.ip remote.port geoip.longitude geoip.latitude geoip.postal_code;
            set req.http.Formkey = regsub(digest.hash_md5(req.http.seed), "^0x", "");
        }
        error 760 req.http.Formkey;
    }

    # geoip lookup
    if (req.url ~ "fastlycdn/esi/getcountry/") {
        # check if GeoIP has been already processed by client
        if (req.http.Cookie:FASTLY_CDN_GEOIP_PROCESSED) {
            error 200 "";
        } else {
            # modify req.url and restart request processing
            error 750 geoip.country_code;
        }
    }

    # geoip get country code
    if (req.url ~ "fastlycdn/esi/getcountrycode/") {
        # create and set req.http.X-Country-Code
        error 755 geoip.country_code;
    }

    # check for ESI calls
    if (req.url ~ "esi_data=") {
        # check for valid cookie data
        if (req.http.Cookie ~ "FASTLY_CDN-([A-Za-z0-9-_]+)=([^;]*)") {
            set req.url = querystring.filter(req.url, "esi_data") + "&esi_data=" + re.group.2;
        }
    }

    # Per suggestions in https://github.com/sdinteractive/SomethingDigital_PageCacheParams
    # we'll strip out query parameters used in Google AdWords, Mailchimp tracking
    set req.http.Magento-Original-URL = req.url;
    set req.url = querystring.regfilter(req.url, "^(utm_.*|gclid|gdftrk|_ga|mc_.*)");

    # If object has been marked as pass pass it
    if ( req.http.X-Pass ) {
        return(pass);
    }


Remove trailing Python code characters from VCL template

sub vcl_recv {
  #FASTLY recv

  if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
    return(pass);
  }

  return(lookup);
}

sub vcl_fetch {
  #FASTLY fetch

  if ((beresp.status == 500 || beresp.status == 503) && req.restarts < 1 && (req.request == "GET" || req.request == "HEAD")) {
    restart;
  }

  if (req.restarts > 0) {
    set beresp.http.Fastly-Restarts = req.restarts;
  }

  if (beresp.http.Set-Cookie) {
    set req.http.Fastly-Cachetype = "SETCOOKIE";
    return(pass);
  }

  if (beresp.http.Cache-Control ~ "private") {
    set req.http.Fastly-Cachetype = "PRIVATE";
    return(pass);
  }

  if (beresp.status == 500 || beresp.status == 503) {
    set req.http.Fastly-Cachetype = "ERROR";
    set beresp.ttl = 1s;
    set beresp.grace = 5s;
    return(deliver);
  }

  if (beresp.http.Expires || beresp.http.Surrogate-Control ~ "max-age" || beresp.http.Cache-Control ~ "(s-maxage|max-age)") {
    # keep the ttl here
  } else {
    # apply the default ttl
    set beresp.ttl = 3600s;
  }

  return(deliver);
}

sub vcl_hit {
  #FASTLY hit

  if (!obj.cacheable) {
    return(pass);
  }
  return(deliver);
}

sub vcl_miss {
  #FASTLY miss
  return(fetch);
}

sub vcl_deliver {
  #FASTLY deliver
  return(deliver);
}

sub vcl_error {
  #FASTLY error
}

sub vcl_pass {
  #FASTLY pass
}

sub vcl_log {
  #FASTLY log
}""")


Enable shielding on tester requests

Enable shielding on tester requests

    # When using Magento tester to test whether your site is configured properly
    # this uses a bypass secret. By default we will use service ID as the bypass secret
    # however user can override this by defining a bypass_secret key in the
    # magentomodule_config edge dictionary
    if ( req.http.bypass-secret ) {
        declare local var.bypass-secret STRING;
        set var.bypass-secret = table.lookup(magentomodule_config, "bypass_secret", "NONE");
        if ( var.bypass-secret == req.http.bypass-secret ) {
            set req.http.x-pass = "1";
            set var.fastly_req_do_shield = false;
            set req.hash_always_miss = true;
        } else if ( var.bypass-secret == "NONE" && req.http.bypass-secret == req.service_id ) {
            set req.http.x-pass = "1";
            set var.fastly_req_do_shield = false;
            set req.hash_always_miss = true;
        } else {
            error 403 "Bypass Secret incorrect";
        }

        if (req.url == "/fastly-io-tester") {
            # IO won't work for PASS requests
            unset req.http.x-pass;
            # IO requires shielding
            set var.fastly_req_do_shield = true;
            set req.hash_always_miss = true;
            set req.http.X-Fastly-Imageopto-Api = "fastly";
        }
    }

    # VCL required to support maintenance mode. Don't maintenance mode admin pages and supporting assets
    if (table.lookup(magentomodule_config, "allow_super_users_during_maint", "0") == "1" &&
        !req.http.Fastly-Client-Ip ~ maint_allowlist &&
        !req.url ~ "^/(index\.php/)?####ADMIN_PATH####/" &&
        !req.url ~ "^/pub/static/") {

        # If we end up here after a restart and there is a ResponseObject it means we got here after error
        # page VCL restart. We shouldn't touch it. Otherwise return a plain 503 error page
        if ( req.restarts > 0 && req.http.ResponseObject ) {
            set req.http.ResponseObject = "970";
        } else {
            error 503 "Maintenance mode";
        }
    }

    unset req.http.x-long-cache;

    # We want to force long cache times on any of the versioned assets
    if (req.url.path ~ "^/static/version\d*/") {
       set req.http.x-long-cache = "1";
    }

    # User's Cookie may contain some Magento Vary items we should vary on
    if (req.http.cookie:X-Magento-Vary) {
        set req.http.X-Magento-Vary = req.http.cookie:X-Magento-Vary;
    } else {
        unset req.http.X-Magento-Vary;
    }

    ############################################################################################################
    # Following code block controls purge by URL. By default we want to protect all URL purges. In general this
    # is addressed by adding Fastly-Purge-Requires-Auth request header in vcl_recv however this runs the risk of
    # exposing API tokens if user attempts to purge non-https URLs. For this reason inside the Magento module
    # we use X-Purge-Token. Unfortunately this breaks purge from the Fastly UI. Therefore in the next code block
    # we check for presence of X-Purge-Token. If it's not present we force the Fastly-Purge-Requires-Auth
    if (req.request == "FASTLYPURGE") {
        # extract token signature and expiration
        if (req.http.X-Purge-Token && req.http.X-Purge-Token ~ "^([^_]+)_(.*)" ) {

            declare local var.X-Exp STRING;
            declare local var.X-Sig STRING;
            /* extract token expiration and signature */
            set var.X-Exp = re.group.1;
            set var.X-Sig = re.group.2;

            /* validate signature */
            if (var.X-Sig == regsub(digest.hmac_sha1(req.service_id, req.url.path var.X-Exp), "^0x", "")) {
            /* check that expiration time has not elapsed */
                if (time.is_after(now, std.integer2time(std.atoi(var.X-Exp)))) {
                    error 410;
                }
            } else {
                error 403;
            }
        } else {
            set req.http.Fastly-Purge-Requires-Auth = "1";
        }
    }

    # set HTTPS header for offloaded TLS
    if (req.http.Fastly-SSL) {
        set req.http.Https = "on";
    }

    if (fastly.ff.visits_this_service > 0) {
        # disable ESI processing on Origin Shield
        set req.esi = false;
        # Needed for proper handling of stale while revalidated when shielding is involved
        set req.max_stale_while_revalidate = 0s;
    }

    # Make sure we lookup end user geo not shielding. More at https://docs.fastly.com/vcl/geolocation/#using-geographic-variables-with-shielding
    set client.geo.ip_override = req.http.Fastly-Client-IP;

    if ((req.request == "GET" || req.request == "HEAD") && req.url.path ~ "/graphql" && req.url.qs ~ "query=") {
        set req.http.graphql = "1";
    } else {
        unset req.http.graphql;
    }

    # geoip lookup
    if (req.url.path ~ "fastlyCdn/geoip/getaction/") {
        # check if GeoIP has been already processed by client. this normally happens before essential cookies are set.
        if (req.http.cookie:X-Magento-Vary || req.http.cookie:form_key) {
            error 980 "GeoIP already processed";
        } else {
            # append parameter with country code only if it doesn't exist already
            if ( req.url.qs !~ "country_code=" ) {
                set req.url = querystring.set(req.url, "country_code", if ( req.http.geo_override, req.http.geo_override, client.geo.country_code));
            }
        }
    } else if ( req.url.path !~ "/graphql" ) {
        # Per suggestions in https://github.com/sdinteractive/SomethingDigital_PageCacheParams
        # we'll strip out query parameters used in Google AdWords, Mailchimp tracking by default
        # and allow custom parameters to be set. List of parameters is configurable in admin
        set req.http.Magento-Original-URL = req.url;
        # Change the list of ignored parameters by configuring them in the Advanced section
        set req.url = querystring.regfilter(req.url, "^(####QUERY_PARAMETERS####)$");
    }

    # Don't allow clients to force a pass
    if (req.restarts == 0) {
        unset req.http.x-pass;
        unset req.http.Rate-Limit;
        unset req.http.magento-admin-path;
    }

    # Pass on checkout URLs. Because it's a snippet we want to execute this after backend selection so we handle it
    # in the request condition
    if (!req.http.x-long-cache && req.url ~ "/(checkout|customer/section/load)") {
        set req.http.x-pass = "1";
    # Pass all admin actions
    # ####ADMIN_PATH#### is replaced with value of frontName from app/etc/env.php
    } else if ( req.url ~ "^/(index\.php/)?####ADMIN_PATH####/" ) {
        set req.http.x-pass = "1";
        set req.http.magento-admin-path = "1";
    } else {
        # Sort the query arguments to increase cache hit ratio with query arguments that
        # may be out od order. In case you want to restore the unsorted URL add a snippet
        # after this one that sets req.url to req.http.Magento-Original-URL
        if ( !req.http.x-pass ) {
            set req.url = boltsort.sort(req.url);
        }
    }

    # static files are always cacheable. remove SSL flag and cookie
    if (req.http.x-long-cache || req.url ~ "^/(pub/)?(media|static)/.*") {
        unset req.http.Https;
        unset req.http.Cookie;
    }

    # GraphQL special headers handling because this area doesn't rely on X-Magento-Vary cookie
    if (req.http.graphql && !req.http.X-Magento-Cache-Id && req.http.Authorization ~ "^Bearer" ) {
        unset req.http.graphql;
        set req.http.x-pass = "1";
    }

    # When using Magento tester to test whether your site is configured properly
    # this uses a bypass secret. By default we will use service ID as the bypass secret
    # however user can override this by defining a bypass_secret key in the
    # magentomodule_config edge dictionary
    if ( req.http.bypass-secret ) {
        declare local var.bypass-secret STRING;
        set var.bypass-secret = table.lookup(magentomodule_config, "bypass_secret", "NONE");
        if ( var.bypass-secret == req.http.bypass-secret ) {
            set req.http.x-pass = "1";
            set var.fastly_req_do_shield = false;
            set req.hash_always_miss = true;
        } else if ( var.bypass-secret == "NONE" && req.http.bypass-secret == req.service_id ) {
            set req.http.x-pass = "1";
            set var.fastly_req_do_shield = false;
            set req.hash_always_miss = true;
        } else {
            error 403 "Bypass Secret incorrect";
        }

        if (req.url == "/fastly-io-tester") {
            # IO won't work for PASS requests
            unset req.http.x-pass;
            set req.hash_always_miss = true;
            set req.http.X-Fastly-Imageopto-Api = "fastly";
        }
    }

    # VCL required to support maintenance mode. Don't maintenance mode admin pages and supporting assets
    if (table.lookup(magentomodule_config, "allow_super_users_during_maint", "0") == "1" &&
        !req.http.Fastly-Client-Ip ~ maint_allowlist &&
        !req.url ~ "^/(index\.php/)?####ADMIN_PATH####/" &&
        !req.url ~ "^/pub/static/") {

        # If we end up here after a restart and there is a ResponseObject it means we got here after error
        # page VCL restart. We shouldn't touch it. Otherwise return a plain 503 error page
        if ( req.restarts > 0 && req.http.ResponseObject ) {
            set req.http.ResponseObject = "970";
        } else {
            error 503 "Maintenance mode";
        }
    }

    unset req.http.x-long-cache;

    # We want to force long cache times on any of the versioned assets
    if (req.url.path ~ "^/static/version\d*/") {
       set req.http.x-long-cache = "1";
    }

    # User's Cookie may contain some Magento Vary items we should vary on
    if (req.http.cookie:X-Magento-Vary) {
        set req.http.X-Magento-Vary = req.http.cookie:X-Magento-Vary;
    } else {
        unset req.http.X-Magento-Vary;
    }

    ############################################################################################################
    # Following code block controls purge by URL. By default we want to protect all URL purges. In general this
    # is addressed by adding Fastly-Purge-Requires-Auth request header in vcl_recv however this runs the risk of
    # exposing API tokens if user attempts to purge non-https URLs. For this reason inside the Magento module
    # we use X-Purge-Token. Unfortunately this breaks purge from the Fastly UI. Therefore in the next code block
    # we check for presence of X-Purge-Token. If it's not present we force the Fastly-Purge-Requires-Auth
    if (req.request == "FASTLYPURGE") {
        # extract token signature and expiration
        if (req.http.X-Purge-Token && req.http.X-Purge-Token ~ "^([^_]+)_(.*)" ) {

            declare local var.X-Exp STRING;
            declare local var.X-Sig STRING;
            /* extract token expiration and signature */
            set var.X-Exp = re.group.1;
            set var.X-Sig = re.group.2;

            /* validate signature */
            if (var.X-Sig == regsub(digest.hmac_sha1(req.service_id, req.url.path var.X-Exp), "^0x", "")) {
            /* check that expiration time has not elapsed */
                if (time.is_after(now, std.integer2time(std.atoi(var.X-Exp)))) {
                    error 410;
                }
            } else {
                error 403;
            }
        } else {
            set req.http.Fastly-Purge-Requires-Auth = "1";
        }
    }

    # set HTTPS header for offloaded TLS
    if (req.http.Fastly-SSL) {
        set req.http.Https = "on";
    }

    if (fastly.ff.visits_this_service > 0) {
        # disable ESI processing on Origin Shield
        set req.esi = false;
        # Needed for proper handling of stale while revalidated when shielding is involved
        set req.max_stale_while_revalidate = 0s;
    }

    # Make sure we lookup end user geo not shielding. More at https://docs.fastly.com/vcl/geolocation/#using-geographic-variables-with-shielding
    set client.geo.ip_override = req.http.Fastly-Client-IP;

    if ((req.request == "GET" || req.request == "HEAD") && req.url.path ~ "/graphql" && req.url.qs ~ "query=") {
        set req.http.graphql = "1";
    } else {
        unset req.http.graphql;
    }

    # geoip lookup
    if (req.url.path ~ "fastlyCdn/geoip/getaction/") {
        # check if GeoIP has been already processed by client. this normally happens before essential cookies are set.
        if (req.http.cookie:X-Magento-Vary || req.http.cookie:form_key) {
            error 980 "GeoIP already processed";
        } else {
            # append parameter with country code only if it doesn't exist already
            if ( req.url.qs !~ "country_code=" ) {
                set req.url = querystring.set(req.url, "country_code", if ( req.http.geo_override, req.http.geo_override, client.geo.country_code));
            }
        }
    } else if ( req.url.path !~ "/graphql" ) {
        # Per suggestions in https://github.com/sdinteractive/SomethingDigital_PageCacheParams
        # we'll strip out query parameters used in Google AdWords, Mailchimp tracking by default
        # and allow custom parameters to be set. List of parameters is configurable in admin
        set req.http.Magento-Original-URL = req.url;
        # Change the list of ignored parameters by configuring them in the Advanced section
        set req.url = querystring.regfilter(req.url, "^(####QUERY_PARAMETERS####)$");
    }

    # Don't allow clients to force a pass
    if (req.restarts == 0) {
        unset req.http.x-pass;
        unset req.http.Rate-Limit;
        unset req.http.magento-admin-path;
    }

    # Pass on checkout URLs. Because it's a snippet we want to execute this after backend selection so we handle it
    # in the request condition
    if (!req.http.x-long-cache && req.url ~ "/(checkout|customer/section/load)") {
        set req.http.x-pass = "1";
    # Pass all admin actions
    # ####ADMIN_PATH#### is replaced with value of frontName from app/etc/env.php
    } else if ( req.url ~ "^/(index\.php/)?####ADMIN_PATH####/" ) {
        set req.http.x-pass = "1";
        set req.http.magento-admin-path = "1";
    } else {
        # Sort the query arguments to increase cache hit ratio with query arguments that
        # may be out od order. In case you want to restore the unsorted URL add a snippet
        # after this one that sets req.url to req.http.Magento-Original-URL
        if ( !req.http.x-pass ) {
            set req.url = boltsort.sort(req.url);
        }
    }

    # static files are always cacheable. remove SSL flag and cookie
    if (req.http.x-long-cache || req.url ~ "^/(pub/)?(media|static)/.*") {
        unset req.http.Https;
        unset req.http.Cookie;
    }

    # GraphQL special headers handling because this area doesn't rely on X-Magento-Vary cookie
    if (req.http.graphql && !req.http.X-Magento-Cache-Id && req.http.Authorization ~ "^Bearer" ) {
        unset req.http.graphql;
        set req.http.x-pass = "1";
    }


Missing X-Wall

Missing X-Wall

backend F_fake_backend {
	.host = "fake-backend.example.com";
	.port = "80";
	.dynamic = true;
	.connect_timeout = 5s;
	.first_byte_timeout = 15s;
	.probe = {
		.dummy = true;
	}
}

sub vcl_recv {
#FASTLY recv
	set req.backend = F_fake_backend;

	set req.http.X-Bottle = if(req.url ~ "(?i)bottle=([^&]*)", urldecode(re.group.1), "99");
	set req.http.X-Wall = if(req.http.X-Wall, req.http.X-Wall " ", "") req.http.X-Bottle;

	if(req.http.X-Bottle == "0") {
		error 996;
	}
	return(lookup);
}

sub vcl_fetch {
#FASTLY fetch

	if(req.restarts > 0) {
		set beresp.http.Fastly-Restarts = req.restarts;
	}
	return(deliver);
}

sub vcl_deliver {
#FASTLY deliver
	set resp.http.X-Wall = req.http.X-Wall;

	if(req.restarts == 2) {
# For now, limit to 3 bottles as we can only restart 3 times
		set req.http.X-Next-Bottle = "0";
	}

	set req.url = "/?bottle=" req.http.X-Next-Bottle;
	if(req.restarts < 2) {
		restart;
	}
	return(deliver);
}

sub set_next_bottle {
	set req.grace = 1d;
	set req.http.X-Original-Grace = req.grace;
	set req.grace = std.atoi(req.http.X-Bottle);
	set req.grace -= 1s;
	set req.http.X-Next-Bottle = regsub(req.grace, "\..*", "");
	set req.grace = std.atoi(req.http.X-Original-Grace);
}

sub vcl_error {
#FASTLY error
	if(obj.status == 996) {
		set obj.status = 200;
		set obj.response = "Cheers!";
		synthetic req.http.X-Wall;
		return (deliver);
	}
}

backend F_fake_backend {
	.host = "fake-backend.example.com";
	.port = "80";
	.dynamic = true;
	.connect_timeout = 5s;
	.first_byte_timeout = 15s;
	.probe = {
		.dummy = true;
	}
}

sub vcl_recv {
#FASTLY recv
	set req.backend = F_fake_backend;

	set req.http.X-Bottle = if(req.url ~ "(?i)bottle=([^&]*)", urldecode(re.group.1), "99");
	set req.http.X-Wall = if(req.http.X-Wall, " ", "") req.http.X-Bottle;

	if(req.http.X-Bottle == "0") {
		error 996;
	}
	return(lookup);
}

sub vcl_fetch {
#FASTLY fetch

	if(req.restarts > 0) {
		set beresp.http.Fastly-Restarts = req.restarts;
	}
	return(deliver);
}

sub vcl_deliver {
#FASTLY deliver
	set resp.http.X-Wall = req.http.X-Wall;

	if(req.restarts == 2) {
# For now, limit to 3 bottles as we can only restart 3 times
		set req.http.X-Next-Bottle = "0";
	}

	set req.url = "/?bottle=" req.http.X-Next-Bottle;
	if(req.restarts < 2) {
		restart;
	}
	return(deliver);
}

sub set_next_bottle {
	set req.grace = 1d;
	set req.http.X-Original-Grace = req.grace;
	set req.grace = std.atoi(req.http.X-Bottle);
	set req.grace -= 1s;
	set req.http.X-Next-Bottle = regsub(req.grace, "\..*", "");
	set req.grace = std.atoi(req.http.X-Original-Grace);
}

sub vcl_error {
#FASTLY error
	if(obj.status == 996) {
		set obj.status = 200;
		set obj.response = "Cheers!";
		synthetic req.http.X-Wall;
		return (deliver);
	}
}


Don't cache /status requests.

Don't cache /status requests.

# TODO:
# - add probe for backend health
backend poky {
  .host = "127.0.0.1";
  .port = "8081";               # make sure this syncs up with the deploy config
  .connect_timeout = 1s;
  .first_byte_timeout = 5s;
  .between_bytes_timeout = 2s;
  .probe = {
      .url = "/status";
      .interval = 5s;
      .timeout = 1s;
      .window = 5;
      .threshold = 3;
  }
}

sub vcl_recv {
    set req.backend = poky;
    set req.grace = 5m;

    # on PUT, POST, or DELETE, we want to invalidate the given object
    if (req.request == "POST" || req.request == "PUT" || req.request == "DELETE") {
        ban("req.url ~ " + req.url);
    }

    if (req.url ~ "^/status$") {
        return (pass);
    }
}

sub vcl_fetch {
    set beresp.grace = 5m;
    if (beresp.status == 200) {
        set beresp.ttl = 1h;
    }

    if (beresp.status == 404) {
        set beresp.ttl = 0s;
    }

    if (beresp.status >= 500) {
        set beresp.ttl = 0s;
    }

}

sub vcl_deliver {
    set resp.http.X-Served-By = server.identity;
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
}

# TODO:
# - add probe for backend health
backend poky {
  .host = "127.0.0.1";
  .port = "8081";               # make sure this syncs up with the deploy config
  .connect_timeout = 1s;
  .first_byte_timeout = 5s;
  .between_bytes_timeout = 2s;
  .probe = {
      .url = "/status";
      .interval = 5s;
      .timeout = 1s;
      .window = 5;
      .threshold = 3;
  }
}

sub vcl_recv {
    set req.backend = poky;
    set req.grace = 5m;

    # on PUT, POST, or DELETE, we want to invalidate the given object
    if (req.request == "POST" || req.request == "PUT" || req.request == "DELETE") {
        ban("req.url ~ " + req.url);
    }
}

sub vcl_fetch {
    set beresp.grace = 5m;
    if (beresp.status == 200) {
        set beresp.ttl = 1h;
    }

    if (beresp.status == 404) {
        set beresp.ttl = 0s;
    }

    if (beresp.status >= 500) {
        set beresp.ttl = 0s;
    }

}

sub vcl_deliver {
    set resp.http.X-Served-By = server.identity;
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
}


Configuration for Varnish.

Configuration for Varnish.

# This is a Varnish 4.x VCL file
vcl 4.0;

# Rails server
backend default {
    .host = "127.0.0.1";
    .port = "3000";
}

Sinatra server
backend sinatra {
    .host = "127.0.0.1";
    .port = "4567";
}

# Node.js server
# backend nodejs {
#     .host = "127.0.0.1";
#     .port = "5678";
# }

# Handle the HTTP request received by the client
sub vcl_recv {
	# if (req.url ~ "^/api/v1") {
	#         set req.backend_hint = default;
	#     }

	if (req.url ~ "^/api/v1/products") {
        set req.backend_hint = sinatra;
    } else if (req.url ~ "^/api/v2/products") {
        set req.backend_hint = sinatra;
    } else {
        set req.backend_hint = default;
    }

	# if (req.url ~ "^/api/v1/products") {
	#         set req.backend_hint = sinatra;
	#     } else if (req.url ~ "^/api/v2/products") {
	#         set req.backend_hint = sinatra;
	#     } else if (req.url ~ "^/api/v3/products") {
	#         set req.backend_hint = nodejs;
	#     } else {
	#         set req.backend_hint = default;
	#     }
}

# This is a Varnish 4.x VCL file
vcl 4.0;

# Rails server
backend default {
    .host = "127.0.0.1";
    .port = "3000";
}

# Sinatra server
# backend sinatra {
#     .host = "127.0.0.1";
#     .port = "4567";
# }

# Node.js server
# backend nodejs {
#     .host = "127.0.0.1";
#     .port = "5678";
# }

# Handle the HTTP request received by the client
sub vcl_recv {
	if (req.url ~ "^/api/v1") {
        set req.backend_hint = default;
    }

	# if (req.url ~ "^/api/v1/products") {
	#         set req.backend_hint = sinatra;
	#     } else if (req.url ~ "^/api/v2/products") {
	#         set req.backend_hint = sinatra;
	#     } else {
	#         set req.backend_hint = default;
	#     }

	# if (req.url ~ "^/api/v1/products") {
	#         set req.backend_hint = sinatra;
	#     } else if (req.url ~ "^/api/v2/products") {
	#         set req.backend_hint = sinatra;
	#     } else if (req.url ~ "^/api/v3/products") {
	#         set req.backend_hint = nodejs;
	#     } else {
	#         set req.backend_hint = default;
	#     }
}


Update fixture

Update fixture

import authorization;

sub vcl_init {
    authorization.database("mongodb");
    authorization.database_connect("127.0.0.1", 27017, "database.collection");
    authorization.database_scheme("token", "secretkey", "ratelimit");
}

sub vcl_recv {
    if (req.http.Authorization && req.http.X-Custom-Date) {
        if(authorization.is_valid(req.http.Authorization, req.url, req.http.X-Custom-Date)){
            return (pass);
        }else{
            error 401 "Not Authorized";
        }
    }
}

import authorization;

sub vcl_init {
    authorization.database("mongodb");
    authorization.database_connect("127.0.0.1", 27017, "database.collection");
    authorization.database_scheme("token", "secretkey");
}

sub vcl_recv {
    if (req.http.Authorization && req.http.X-Custom-Date) {
        if(authorization.is_valid(req.http.Authorization, req.url, req.http.X-NYTV-Date)){
            return (pass);
        }else{
            error 401 "Not Authorized";
        }
    }
}


Update zope-plone.vcl to v3 syntax

Update zope-plone.vcl to v3 syntax

Thanks to cleberjsantos for initial patch which this is based on.

Fixes: #976

#
# This is an example VCL configuration file for varnish, meant for the
# Plone CMS running within Zope.  It defines a "default" backend for
# serving static content from a normal web server, and a "zope"
# backend for requests to the Zope CMS
#
# See the vcl(7) man page for details on VCL syntax and semantics.
#
# $Id$
#

# Default backend definition.  Set this to point to your content
# server.

# Default backend is the Zope CMS
backend default {
	.host = "127.0.0.1";
	.port = "9673";
}

acl purge {
	"localhost";
	"192.0.2.0"/24;
}

sub vcl_recv {

	# Normalize host headers, and do rewriting for the zope sites.  Reject
	# requests for unknown hosts
        if (req.http.host ~ "(www.)?example.com") {
                set req.http.host = "example.com";
                set req.url = "/VirtualHostBase/http/example.com:80/example.com/VirtualHostRoot" + req.url;
        } elsif (req.http.host ~ "(www.)?example.org") {
                set req.http.host = "example.org";
                set req.url = "/VirtualHostBase/http/example.org:80/example.org/VirtualHostRoot" + req.url;
        } else {
                error 404 "Unknown virtual host.";
        }

        # Handle special requests
        if (req.request != "GET" && req.request != "HEAD") {

                # POST - Logins and edits
                if (req.request == "POST") {
                        return(pass);
                }

                # PURGE - The CacheFu product can invalidate updated URLs
                if (req.request == "PURGE") {
                        if (!client.ip ~ purge) {
                                error 405 "Not allowed.";
                        }
                        return(lookup);
                }
        }

        # Don't cache authenticated requests
        if (req.http.Cookie && req.http.Cookie ~ "__ac(|_(name|password|persistent))=") {

		# Force lookup of specific urls unlikely to need protection
		if (req.url ~ "\.(js|css)") {
                        remove req.http.cookie;
                        return(lookup);
                }
                return(pass);
        }

        # The default vcl_recv is used from here.
 }

# Do the PURGE thing
sub vcl_hit {
        if (req.request == "PURGE") {
                purge;
                error 200 "Purged";
        }
}
sub vcl_miss {
        if (req.request == "PURGE") {
                purge;
                error 200 "Purged";
        }
}

# Enforce a minimum TTL, since we can PURGE changed objects actively
# from Zope by using the CacheFu product

sub vcl_fetch {
        if (beresp.ttl < 3600s) {
                set beresp.ttl = 3600s;
        }
}

#
# This is an example VCL configuration file for varnish, meant for the
# Plone CMS running within Zope.  It defines a "default" backend for
# serving static content from a normal web server, and a "zope"
# backend for requests to the Zope CMS
#
# See the vcl(7) man page for details on VCL syntax and semantics.
#
# $Id$
#

# Default backend definition.  Set this to point to your content
# server.

# Default backend is the Zope CMS
backend default {
	set backend.host = "127.0.0.1";
	set backend.port = "9673";
}

acl purge {
	"localhost";
	"192.0.2.0"/24;
}

sub vcl_recv {

	# Normalize host headers, and do rewriting for the zope sites.  Reject
	# requests for unknown hosts
        if (req.http.host ~ "(www.)?example.com") {
                set req.http.host = "example.com";
                set req.url = "/VirtualHostBase/http/example.com:80/example.com/VirtualHostRoot" req.url;
        } elsif (req.http.host ~ "(www.)?example.org") {
                set req.http.host = "example.org";
                set req.url = "/VirtualHostBase/http/example.org:80/example.org/VirtualHostRoot" req.url;
        } else {
                error 404 "Unknown virtual host.";
        }

        # Handle special requests
        if (req.request != "GET" && req.request != "HEAD") {

                # POST - Logins and edits
                if (req.request == "POST") {
                        pass;
                }

                # PURGE - The CacheFu product can invalidate updated URLs
                if (req.request == "PURGE") {
                        if (!client.ip ~ purge) {
                                error 405 "Not allowed.";
                        }
                        lookup;
                }
        }

        # Don't cache authenticated requests
        if (req.http.Cookie && req.http.Cookie ~ "__ac(|_(name|password|persistent))=") {

		# Force lookup of specific urls unlikely to need protection
		if (req.url ~ "\.(js|css)") {
                        remove req.http.cookie;
                        lookup;
                }
                pass;
        }

        # The default vcl_recv is used from here.
 }

# Do the PURGE thing
sub vcl_hit {
        if (req.request == "PURGE") {
                set obj.ttl = 0s;
                error 200 "Purged";
        }
}
sub vcl_miss {
        if (req.request == "PURGE") {
                error 404 "Not in cache";
        }
}

# Enforce a minimum TTL, since we can PURGE changed objects actively
# from Zope by using the CacheFu product

sub vcl_fetch {
        if (obj.ttl < 3600s) {
                set obj.ttl = 3600s;
        }
}


Another comment

Another comment

sub vcl_recv {
  if (req.restarts < 1) {
    # Sanitise header
    unset req.http.X-eLife-Restart;
  }

  #FASTLY recv

  if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
    return(pass);
  }

  return(lookup);
}

sub vcl_fetch {
  #FASTLY fetch

  if (beresp.status >= 500 && beresp.status < 600) {
    if (stale.exists) {
      return(deliver_stale);
    }

    if (req.restarts < 1 && (req.request == "GET" || req.request == "HEAD")) {
      set req.http.X-eLife-Restart = "fetch," beresp.status;
      unset req.http.Cookie; # Temporarily log out the user

      restart;
    }

    if (!beresp.http.Content-Length || beresp.http.Content-Length == "0") {
      # Elastic Load Balancer returns empty error responses
      error beresp.status;
    }
  }

  if (req.restarts > 0) {
    set beresp.http.Fastly-Restarts = req.restarts;
  }

  if (beresp.http.Set-Cookie) {
    set req.http.Fastly-Cachetype = "SETCOOKIE";
    return(pass);
  }

  if (beresp.http.Cache-Control ~ "private") {
    set req.http.Fastly-Cachetype = "PRIVATE";
    return(pass);
  }

  if (beresp.status == 500 || beresp.status == 503) {
    set req.http.Fastly-Cachetype = "ERROR";
    set beresp.ttl = 1s;
    set beresp.grace = 5s;
    return(deliver);
  }

  if (beresp.http.Expires || beresp.http.Surrogate-Control ~ "max-age" || beresp.http.Cache-Control ~ "(s-maxage|max-age)") {
    # keep the ttl here
  } else {
    # apply the default ttl
    set beresp.ttl = 3600s;
  }

  return(deliver);
}

sub vcl_hit {
  #FASTLY hit

  if (!obj.cacheable) {
    return(pass);
  }
  return(deliver);
}

sub vcl_miss {
  #FASTLY miss
  return(fetch);
}

sub vcl_deliver {
  if (resp.status >= 500 && resp.status < 600 && stale.exists) {
    set req.http.X-eLife-Restart = "deliver," resp.status;

    restart;
  }

  if (req.http.Fastly-Debug && req.http.X-eLife-Restart) {
    set resp.http.X-eLife-Restart = req.http.X-eLife-Restart;
  }

  #FASTLY deliver
  return(deliver);
}

sub vcl_error {
  if (obj.status >= 500 && obj.status < 600 && stale.exists) {
    return(deliver_stale);
  }

  #FASTLY error
}

sub vcl_pass {
  #FASTLY pass
}

sub vcl_log {
  #FASTLY log
}

sub vcl_recv {
  if (req.restarts < 1) {
    # Sanitise header
    unset req.http.X-eLife-Restart;
  }

  #FASTLY recv

  if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
    return(pass);
  }

  return(lookup);
}

sub vcl_fetch {
  #FASTLY fetch

  if (beresp.status >= 500 && beresp.status < 600) {
    if (stale.exists) {
      return(deliver_stale);
    }

    if (req.restarts < 1 && (req.request == "GET" || req.request == "HEAD")) {
      set req.http.X-eLife-Restart = "fetch," beresp.status;
      unset req.http.Cookie; # Temporarily log out the user

      restart;
    }

    if (!beresp.http.Content-Length || beresp.http.Content-Length == "0") {
      error beresp.status;
    }
  }

  if (req.restarts > 0) {
    set beresp.http.Fastly-Restarts = req.restarts;
  }

  if (beresp.http.Set-Cookie) {
    set req.http.Fastly-Cachetype = "SETCOOKIE";
    return(pass);
  }

  if (beresp.http.Cache-Control ~ "private") {
    set req.http.Fastly-Cachetype = "PRIVATE";
    return(pass);
  }

  if (beresp.status == 500 || beresp.status == 503) {
    set req.http.Fastly-Cachetype = "ERROR";
    set beresp.ttl = 1s;
    set beresp.grace = 5s;
    return(deliver);
  }

  if (beresp.http.Expires || beresp.http.Surrogate-Control ~ "max-age" || beresp.http.Cache-Control ~ "(s-maxage|max-age)") {
    # keep the ttl here
  } else {
    # apply the default ttl
    set beresp.ttl = 3600s;
  }

  return(deliver);
}

sub vcl_hit {
  #FASTLY hit

  if (!obj.cacheable) {
    return(pass);
  }
  return(deliver);
}

sub vcl_miss {
  #FASTLY miss
  return(fetch);
}

sub vcl_deliver {
  if (resp.status >= 500 && resp.status < 600 && stale.exists) {
    set req.http.X-eLife-Restart = "deliver," resp.status;

    restart;
  }

  if (req.http.Fastly-Debug && req.http.X-eLife-Restart) {
    set resp.http.X-eLife-Restart = req.http.X-eLife-Restart;
  }

  #FASTLY deliver
  return(deliver);
}

sub vcl_error {
  if (obj.status >= 500 && obj.status < 600 && stale.exists) {
    return(deliver_stale);
  }

  #FASTLY error
}

sub vcl_pass {
  #FASTLY pass
}

sub vcl_log {
  #FASTLY log
}


Remove unnecessary restart block since it's never gonna get hit

    /* handle 5XX (or any other unwanted status code) */
    if (beresp.status >= 500 && beresp.status < 600) {

        # let SOAP errors pass - better debugging
        if (beresp.http.Content-Type ~ "text/xml") {
            return (deliver);
        }

        /* deliver stale if the object is available */
        if (stale.exists) {
        return(deliver_stale);
        }

        if (req.restarts < 1 && (req.request == "GET" || req.request == "HEAD")) {
        restart;
        }

        /* else go to vcl_error to deliver a synthetic */
        error 503;
    }

    # Remove Set-Cookies from responses for static content
    # to match the cookie removal in recv.
    if (req.url ~ "^/(pub/)?(media|static)/") {
        unset beresp.http.set-cookie;

        # Set a short TTL for 404's since those can be temporary during the site build/index
        if (beresp.status == 404) {
            set beresp.ttl = 300s;
        }

    }

    if (req.restarts > 0 ) {
        set beresp.http.Fastly-Restarts = req.restarts;
    }

    if (beresp.http.Content-Type ~ "text/html" || beresp.http.Content-Type ~ "text/xml") {
        # enable ESI feature for Magento response by default
        esi;
        if (!beresp.http.Vary ~ "X-Magento-Vary,Https") {
            if (beresp.http.Vary) {
                    set beresp.http.Vary = beresp.http.Vary ",X-Magento-Vary,Https";
                } else {
                    set beresp.http.Vary = "X-Magento-Vary,Https";
                }
        }
        # Since varnish doesn't compress ESIs we need to hint to the HTTP/2 terminators to
        # compress it
        set beresp.http.x-compress-hint = "on";
    } else {
        # enable gzip for all static content
        if ((beresp.status == 200 || beresp.status == 404) && (beresp.http.content-type ~ "^(application\/x\-javascript|text\/css|application\/javascript|text\/javascript|application\/json|application\/vnd\.ms\-fontobject|application\/x\-font\-opentype|application\/x\-font\-truetype|application\/x\-font\-ttf|application\/xml|font\/eot|font\/opentype|font\/otf|image\/svg\+xml|image\/vnd\.microsoft\.icon|text\/plain)\s*($|;)" || req.url ~ "\.(css|js|html|eot|ico|otf|ttf|json)($|\?)" ) ) {
            # always set vary to make sure uncompressed versions dont always win
            if (!beresp.http.Vary ~ "Accept-Encoding") {
                if (beresp.http.Vary) {
                    set beresp.http.Vary = beresp.http.Vary ", Accept-Encoding";
                } else {
                    set beresp.http.Vary = "Accept-Encoding";
                }
            }
            if (req.http.Accept-Encoding == "gzip") {
                set beresp.gzip = true;
            }
        }
    }

    # cache only successfully responses and 404s
    if (beresp.status != 200 && beresp.status != 301 && beresp.status != 404) {
        set req.http.Fastly-Cachetype = "ERROR";
        set beresp.ttl = 1s;
        set beresp.grace = 5s;
        return (deliver);
    } elsif (beresp.http.Cache-Control ~ "private") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

    if (beresp.http.X-Magento-Debug) {
        set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;
    }

    # validate if we need to cache it and prevent from setting cookie
    # images, css and js are cacheable by default so we have to remove cookie also
    if (beresp.ttl > 0s && (req.request == "GET" || req.request == "HEAD")) {
        unset beresp.http.set-cookie;
        if (req.url !~ "^/(pub/)?(media|static)/.*") {
            set beresp.grace = 86400m;
        }

        # init surrogate keys
        if (beresp.http.X-Magento-Tags) {
            set beresp.http.Surrogate-Key = beresp.http.X-Magento-Tags " text";
        } else {
            set beresp.http.Surrogate-Key = "text";
        }

        # set surrogate keys by content type
        if (beresp.http.Content-Type ~ "image") {
            set beresp.http.Surrogate-Key = "image";
        } elsif (beresp.http.Content-Type ~ "script") {
            set beresp.http.Surrogate-Key = "script";
        } elsif (beresp.http.Content-Type ~ "css") {
            set beresp.http.Surrogate-Key = "css";
        }

        set beresp.http.X-Surrogate-Key = beresp.http.Surrogate-Key;
        return (deliver);
    }

    /* handle 5XX (or any other unwanted status code) */
    if (beresp.status >= 500 && beresp.status < 600) {

        /* deliver stale if the object is available */
        if (stale.exists) {
        return(deliver_stale);
        }

        if (req.restarts < 1 && (req.request == "GET" || req.request == "HEAD")) {
        restart;
        }

        /* else go to vcl_error to deliver a synthetic */
        error 503;
    }

    # Remove Set-Cookies from responses for static content
    # to match the cookie removal in recv.
    if (req.url ~ "^/(pub/)?(media|static)/") {
        unset beresp.http.set-cookie;

        # Set a short TTL for 404's
        if (beresp.status == 404) {
            set beresp.ttl = 300s;
        }

    }

    if (beresp.status >= 500) {
        # let SOAP errors pass - better debugging
        if (beresp.http.Content-Type ~ "text/xml") {
            return (deliver);
        }

        if (req.restarts < 1 && (req.request == "GET" || req.request == "HEAD")) {
            restart;
        }
    }

    if (req.restarts > 0 ) {
        set beresp.http.Fastly-Restarts = req.restarts;
    }

    if (beresp.http.Content-Type ~ "text/html" || beresp.http.Content-Type ~ "text/xml") {
        # enable ESI feature for Magento response by default
        esi;
        if (!beresp.http.Vary ~ "X-Magento-Vary,Https") {
            if (beresp.http.Vary) {
                    set beresp.http.Vary = beresp.http.Vary ",X-Magento-Vary,Https";
                } else {
                    set beresp.http.Vary = "X-Magento-Vary,Https";
                }
        }
        # Since varnish doesn't compress ESIs we need to hint to the HTTP/2 terminators to
        # compress it
        set beresp.http.x-compress-hint = "on";
    } else {
        # enable gzip for all static content
        if ((beresp.status == 200 || beresp.status == 404) && (beresp.http.content-type ~ "^(application\/x\-javascript|text\/css|application\/javascript|text\/javascript|application\/json|application\/vnd\.ms\-fontobject|application\/x\-font\-opentype|application\/x\-font\-truetype|application\/x\-font\-ttf|application\/xml|font\/eot|font\/opentype|font\/otf|image\/svg\+xml|image\/vnd\.microsoft\.icon|text\/plain)\s*($|;)" || req.url ~ "\.(css|js|html|eot|ico|otf|ttf|json)($|\?)" ) ) {
            # always set vary to make sure uncompressed versions dont always win
            if (!beresp.http.Vary ~ "Accept-Encoding") {
                if (beresp.http.Vary) {
                    set beresp.http.Vary = beresp.http.Vary ", Accept-Encoding";
                } else {
                    set beresp.http.Vary = "Accept-Encoding";
                }
            }
            if (req.http.Accept-Encoding == "gzip") {
                set beresp.gzip = true;
            }
        }
    }

    # cache only successfully responses and 404s
    if (beresp.status != 200 && beresp.status != 301 && beresp.status != 404) {
        set req.http.Fastly-Cachetype = "ERROR";
        set beresp.ttl = 1s;
        set beresp.grace = 5s;
        return (deliver);
    } elsif (beresp.http.Cache-Control ~ "private") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

    if (beresp.http.X-Magento-Debug) {
        set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;
    }

    # validate if we need to cache it and prevent from setting cookie
    # images, css and js are cacheable by default so we have to remove cookie also
    if (beresp.ttl > 0s && (req.request == "GET" || req.request == "HEAD")) {
        unset beresp.http.set-cookie;
        if (req.url !~ "^/(pub/)?(media|static)/.*") {
            set beresp.grace = 86400m;
        }

        # init surrogate keys
        if (beresp.http.X-Magento-Tags) {
            set beresp.http.Surrogate-Key = beresp.http.X-Magento-Tags " text";
        } else {
            set beresp.http.Surrogate-Key = "text";
        }

        # set surrogate keys by content type
        if (beresp.http.Content-Type ~ "image") {
            set beresp.http.Surrogate-Key = "image";
        } elsif (beresp.http.Content-Type ~ "script") {
            set beresp.http.Surrogate-Key = "script";
        } elsif (beresp.http.Content-Type ~ "css") {
            set beresp.http.Surrogate-Key = "css";
        }

        set beresp.http.X-Surrogate-Key = beresp.http.Surrogate-Key;
        return (deliver);
    }


Minor refinement for basic auth

Minor refinement for basic auth

  # Check Basic auth against a table
  if ( table.lookup(magentomodule_basic_auth, regsub(req.http.Authorization, "^Basic ", ""), "NOTFOUND") == "NOTFOUND" ) {
      error 971;
  }

  if ( table.lookup(magentomodule_basic_auth, req.http.Authorization, "NOTFOUND") == "NOTFOUND" ) {
      error 971;
  }


converted tabs to spaces

converted tabs to spaces

backend origin {
    .host = "localhost";
    .port = "8080";
}

sub vcl_recv {
    # only using one backend
    set req.backend = origin;

    # set standard proxied ip header for getting original remote address
    set req.http.X-Forwarded-For = client.ip;

    # logged in users must always pass
    if( req.url ~ "^/wp-(login|admin)" || req.http.Cookie ~ "wordpress_logged_in_" ){
        return (pass);
    }

    # don't cache search results
    if( req.url ~ "\?s=" ){
        return (pass);
    }

    # always pass through posted requests and those with basic auth
    if ( req.request == "POST" || req.http.Authorization ) {
         return (pass);
    }

    # else ok to fetch a cached page
    unset req.http.Cookie;
    return (lookup);
}

sub vcl_fetch {

    # remove some headers we never want to see
    unset beresp.http.Server;
    unset beresp.http.X-Powered-By;

    # only allow cookies to be set if we're in admin area - i.e. commenters stay logged out
    if( beresp.http.Set-Cookie && req.url !~ "^/wp-(login|admin)" ){
        unset beresp.http.Set-Cookie;
    }

    # don't cache response to posted requests or those with basic auth
    if ( req.request == "POST" || req.http.Authorization ) {
         return (pass);
    }

    # Trust Varnish if it says this is not cacheable
    if ( ! beresp.cacheable ) {
         return (pass);
     }

    # only cache status ok
    if ( beresp.status != 200 ) {
        return (pass);
    }

        # don't cache search results
        if( req.url ~ "\?s=" ){
            return (pass);
        }

    # else ok to cache the response
    set beresp.ttl = 24h;
    return (deliver);
}

sub vcl_deliver {
    # add debugging headers, so we can see what's cached
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
     }
     else {
        set resp.http.X-Cache = "MISS";
    }
    # remove some headers added by varnish
    unset resp.http.Via;
    unset resp.http.X-Varnish;
}

sub vcl_hash {
    set req.hash += req.url;
    # altering hash so subdomains are ignored.
    # don't do this if you actually run different sites on different subdomains
    if ( req.http.host ) {
        set req.hash += regsub( req.http.host, "^([^\.]+\.)+([a-z]+)$", "\1\2" );
    } else {
        set req.hash += server.ip;
    }
    # ensure separate cache for mobile clients (WPTouch workaround)
    if( req.http.User-Agent ~ "(iPod|iPhone|incognito|webmate|dream|CUPCAKE|WebOS|blackberry9\d\d\d)" ){
        set req.hash += "touch";
    }
    return (hash);
}


Add commands

Add commands

# Voice commands for atom

include folders.vch;
include letters.vch;

Go Strawberry = {Ctrl+Alt+F8};
Go Blueberry ={Ctrl+Alt+o};
Go Banana = {Ctrl+Alt+F7};

Start Far = 'var ';

# Commands for developing Atom: init.config, packages; also for navigating existing packages/commands
Reload Atom = {Ctrl+s} Wait(100)  {Shift+Ctrl+Alt+F5};
Command Palette = {Ctrl+Shift+p};
Keyboard Shortcuts = {Ctrl+.};
Run Specs = {Ctrl+Alt+p};
Show Console = {Ctrl+Alt+i};

# Commands for developing D3: D3 visualizations, D3 Sandwiches
Save and Reload =  {Ctrl+s}  Wait(100) SendSystemKeys("{Alt+Tab}")  {Ctrl+r};
Save and Really Reload =  {Ctrl+s}  Wait(100) SendSystemKeys("{Alt+Tab}") {Ctrl+Shift+r};

# File commands
Find (File | Document) = {Ctrl+t};
Save As = {Ctrl+Shift+s};

# --- Navigation commands ------------------------------------
<delimiters> := (Quote = '"' | 'Single Quote' = "'" | Comma = ',' | 'Equal Sign' = '=' | Equals = '='
	| Period = '.' | Colon = ':' | 'Semi-Colon' = ';' | Hyphen = '-' |  Underscore = '_' | Slash = '/'
	| Paren = "(" | "Close Paren" = ")" | Parentheses = "(" | "Close Parentheses" = ")"
	| Bracket = '[' | "Close Bracket" = ']' | Brace = '{' | 'Close Brace' = '}'	);

Line 1..200 = {Ctrl+g} $1 {Enter};
(Left = 'b' | Right = 'f') Word = {Alt+$1};
Top = {Ctrl+Home};
Bottom = {Ctrl+End};
(Find = 'Right' | 'Insert After' = 'Right' | 'Insert Before' = 'Left') (Next = 'Enter' | Last = 'Shift+F3') <delimiters>
		= {Ctrl+f} $3 {$2} {Esc}   Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') <delimiters> =  {Ctrl+f} $2 {Enter} {Esc}   Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') 1..20 <delimiters> =  {Ctrl+f} $3 {Enter} {Esc}   Wait(100) {F3_$2} {Shift+F3} Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') Enda <delimiters> = {End} {Ctrl+f} $2 {Enter} {Esc} {Shift+F3}  Wait(100) {$1};

Fine Alpha <letters> = {Ctrl+f} $1 {Enter} {Esc}   Wait(100) {Left};
Insert (Before = 'Left' | After = 'Right') Alpha <letters> =  {Ctrl+f} $2 {Enter} {Esc}   Wait(100) {$1};
Fine Number 0..9 = {Ctrl+f} $1 {Enter} {Esc} Wait(100) {Right};
Find Again = {F3} {Right};
Find Again 1..20 Times = {F3_$1} {Right};

Find Symbol = {Ctrl+r};

(Set | Add | Clear | Hide | Delete) Bookmark = {Ctrl+Alt+F2};
(Next = 'F2' | Last = 'Shift+F2') Bookmark = {$1};

Next Window = {Ctrl+Tab};
Last Window = {Ctrl+Shift+Tab};

# --- Editing commands ----------------------------------------
Select Line = {Ctrl+l};
Select (Next = 'Down' | Forward = 'Down' | Last = 'Up' | Back = 'Up' | Previous = 'Up') 1..20 Lines = {Home} {Shift+$1_$2};
Grab 1..20 Lines = {Home} {Shift+Down_$1};
Add 1..20 (Lines = 'Down' | Line = 'Down' | Pages = 'PgDn' | Page = 'PgDn') = {Shift+$2_$1};

(Join = 'j' | Indent = ']' | Dedent = '[')  That = {Ctrl+$1};
Move Line (Up | Down) 1..20  = {Ctrl+$1_$2};

Delete Word = {Ctrl+Del};
Delete 1..20 Words = {Ctrl+Del_$1};
Delete (Back | Last) Word = {Ctrl+Backspace};
Delete (Back | Last) 1..20 Words = {Ctrl+Backspace_$2};
Delete Enda Word = {End} {Ctrl+Backspace};

(Delete Line | Dede) = {Ctrl+Shift+k};
Delete 1..20 Lines = {Ctrl+Shift+k_$1};
(Cut = 'x' | Paste = 'v' | Copy = 'c') That = {Ctrl+$1};

Comment (That | Line) = {Ctrl+/};
Comment 1..20 Lines = {Home} {Shift+Down_$1} {Ctrl+/};

Select between (Delimiters | Brackets | Tags) ={Ctrl+m} {Ctrl+Alt+m};

Delete Tags = {Ctrl+Alt+F4};

Replace = {Ctrl+f};
Replace All = {Ctrl+Enter};

# --- Code commands -------------------------------------------
Start (Item = 'li' | List = 'ul' | HTML = 'html' | pre = "pre"
	| div = 'div' | 'Comment' = 'comment-html'
	| 'Bold ID' = 'bold-id' | 'Input' = 'ds-input-text'
	| 'Fix Line' = 'fix-current-line' | 'Fix Current Line' = 'fix-current-line'
	| Snippet = 'my-snippet' | 'Recipe Row' = 'recipe-row' | 'Recipe Play' = 'recipe-play') = $1 {Tab};
Stop (Comment = 'comment-html-end' ) = $1 {Tab};
Start (para = '<p>' ) = $1;
Add (para = 'p') =  '<' $1 '>' {End} '</' $1 '>';
Replace With (para = 'p') = {Ctrl+Alt+F4} '<' $1 '>' {End} '</' $1 '>';

Start (Header | Heading) 1..7 = h $2 {Tab};
Add (Header | Heading) 1..7 =  '<h' $2 '>' {End} '</h' $2 '>';
Replace With (Header | Heading) 1..7 = {Ctrl+Alt+F4}  '<h' $2 '>' {End} '</h' $2 '>' {Home};
Mid HREF = '">';

Paste Function = 'function ' {Ctrl+v} ' {' {Enter} {Enter} '};' {Up};

Pre-Format Code =  {Ctrl+Alt+F5};

# TO ADD:
# adding HTML comments, using a snippet

# Voice commands for atom

include folders.vch;
include letters.vch;

Go Strawberry = {Ctrl+Alt+F8};
Go Blueberry ={Ctrl+Alt+o};
Go Banana = {Ctrl+Alt+F7};

# Commands for developing Atom: init.config, packages; also for navigating existing packages/commands
Reload Atom = {Ctrl+s} Wait(100)  {Shift+Ctrl+Alt+F5};
Command Palette = {Ctrl+Shift+p};
Keyboard Shortcuts = {Ctrl+.};
Run Specs = {Ctrl+Alt+p};
Show Console = {Ctrl+Alt+i};

# Commands for developing D3: D3 visualizations, D3 Sandwiches
Save and Reload =  {Ctrl+s}  Wait(100) SendSystemKeys("{Alt+Tab}")  {Ctrl+r};
Save and Really Reload =  {Ctrl+s}  Wait(100) SendSystemKeys("{Alt+Tab}") {Ctrl+Shift+r};

# File commands
Find (File | Document) = {Ctrl+t};
Save As = {Ctrl+Shift+s};

# --- Navigation commands ------------------------------------
<delimiters> := (Quote = '"' | 'Single Quote' = "'" | Comma = ',' | 'Equal Sign' = '=' | Equals = '='
	| Period = '.' | Colon = ':' | 'Semi-Colon' = ';' | Hyphen = '-' |  Underscore = '_' | Slash = '/'
	| Paren = "(" | "Close Paren" = ")" | Parentheses = "(" | "Close Parentheses" = ")"
	| Bracket = '[' | "Close Bracket" = ']' | Brace = '{' | 'Close Brace' = '}'	);

Line 1..200 = {Ctrl+g} $1 {Enter};
(Left = 'b' | Right = 'f') Word = {Alt+$1};
Top = {Ctrl+Home};
Bottom = {Ctrl+End};
(Find = 'Right' | 'Insert After' = 'Right' | 'Insert Before' = 'Left') (Next = 'Enter' | Last = 'Shift+F3') <delimiters>
		= {Ctrl+f} $3 {$2} {Esc}   Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') <delimiters> =  {Ctrl+f} $2 {Enter} {Esc}   Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') 1..20 <delimiters> =  {Ctrl+f} $3 {Enter} {Esc}   Wait(100) {F3_$2} {Shift+F3} Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') Enda <delimiters> = {End} {Ctrl+f} $2 {Enter} {Esc} {Shift+F3}  Wait(100) {$1};

Fine Alpha <letters> = {Ctrl+f} $1 {Enter} {Esc}   Wait(100) {Left};
Insert (Before = 'Left' | After = 'Right') Alpha <letters> =  {Ctrl+f} $2 {Enter} {Esc}   Wait(100) {$1};
Fine Number 0..9 = {Ctrl+f} $1 {Enter} {Esc} Wait(100) {Right};
Find Again = {F3} {Right};
Find Again 1..20 Times = {F3_$1} {Right};

Find Symbol = {Ctrl+r};

(Set | Add | Clear | Hide | Delete) Bookmark = {Ctrl+Alt+F2};
(Next = 'F2' | Last = 'Shift+F2') Bookmark = {$1};

Next Window = {Ctrl+Tab};
Last Window = {Ctrl+Shift+Tab};

# --- Editing commands ----------------------------------------
Select Line = {Ctrl+l};
Select (Next = 'Down' | Forward = 'Down' | Last = 'Up' | Back = 'Up' | Previous = 'Up') 1..20 Lines = {Home} {Shift+$1_$2};
Grab 1..20 Lines = {Home} {Shift+Down_$1};
Add 1..20 (Lines = 'Down' | Line = 'Down' | Pages = 'PgDn' | Page = 'PgDn') = {Shift+$2_$1};

(Join = 'j' | Indent = ']' | Dedent = '[')  That = {Ctrl+$1};
Move Line (Up | Down) 1..20  = {Ctrl+$1_$2};

Delete Word = {Ctrl+Del};
Delete 1..20 Words = {Ctrl+Del_$1};
Delete (Back | Last) Word = {Ctrl+Backspace};
Delete (Back | Last) 1..20 Words = {Ctrl+Backspace_$2};
Delete Enda Word = {End} {Ctrl+Backspace};

(Delete Line | Dede) = {Ctrl+Shift+k};
Delete 1..20 Lines = {Ctrl+Shift+k_$1};
(Cut = 'x' | Paste = 'v' | Copy = 'c') That = {Ctrl+$1};

Comment (That | Line) = {Ctrl+/};
Comment 1..20 Lines = {Home} {Shift+Down_$1} {Ctrl+/};

Select between (Delimiters | Brackets | Tags) ={Ctrl+m} {Ctrl+Alt+m};

Delete Tags = {Ctrl+Alt+F4};

Replace = {Ctrl+f};
Replace All = {Ctrl+Enter};

# --- Code commands -------------------------------------------
Start (Item = 'li' | List = 'ul' | HTML = 'html' | pre = "pre"
	| div = 'div' | 'Comment' = 'comment-html'
	| 'Bold ID' = 'bold-id' | 'Input' = 'ds-input-text'
	| 'Fix Line' = 'fix-current-line' | 'Fix Current Line' = 'fix-current-line'
	| Snippet = 'my-snippet' | 'Recipe Row' = 'recipe-row' | 'Recipe Play' = 'recipe-play') = $1 {Tab};
Stop (Comment = 'comment-html-end' ) = $1 {Tab};
Start (para = '<p>' ) = $1;
Add (para = 'p') =  '<' $1 '>' {End} '</' $1 '>';
Replace With (para = 'p') = {Ctrl+Alt+F4} '<' $1 '>' {End} '</' $1 '>';

Start (Header | Heading) 1..7 = h $2 {Tab};
Add (Header | Heading) 1..7 =  '<h' $2 '>' {End} '</h' $2 '>';
Replace With (Header | Heading) 1..7 = {Ctrl+Alt+F4}  '<h' $2 '>' {End} '</h' $2 '>' {Home};
Mid HREF = '">';

Paste Function = 'function ' {Ctrl+v} ' {' {Enter} {Enter} '};' {Up};

Pre-Format Code =  {Ctrl+Alt+F5};

# TO ADD:
# adding HTML comments, using a snippet