Add timestamp validation

Add timestamp validation

table consumer_secrets {
	"foo": "super_secret"
}

table access_tokens {
	"bar": "token_secret"
}

sub vcl_recv {
#FASTLY recv
#DEPLOY recv
	set req.http.X-OAuth-Consumer-Key = if(req.url ~ "(?i)oauth_consumer_key=([^&]*)", urldecode(re.group.1), "");
	if(req.http.X-OAuth-Consumer-Key == "") {
		error 401 "Missing Consumer Key";
	}
	set req.http.X-OAuth-Consumer-Secret = table.lookup(consumer_secrets, req.http.X-OAuth-Consumer-Key);
	if(!req.http.X-OAuth-Consumer-Secret) {
		error 401 "Invalid Consumer Key";
	}
	set req.http.X-OAuth-Access-Token = if(req.url ~ "(?i)oauth_token=([^&]*)", urldecode(re.group.1), "");
	if(req.http.X-OAuth-Access-Token != "") {
		set req.http.X-OAuth-Access-Token-Secret = table.lookup(access_tokens, req.http.X-OAuth-Access-Token);
		if(!req.http.X-OAuth-Access-Token-Secret) {
			error 401 "Invalid Access Token";
		}
	} else {
		set req.http.X-OAuth-Access-Token-Secret = "";
	}
	set req.http.X-OAuth-Provided-Signature = if(req.url ~ "(?i)oauth_signature=([^&]*)", urldecode(re.group.1), "");
	if(req.http.X-OAuth-Provided-Signature == "") {
		error 401 "Missing Signature";
	}
	set req.http.X-OAuth-Ordered-Url = boltsort.sort(req.url);
	set req.http.X-OAuth-Parameters = regsub(regsub(req.http.X-OAuth-Ordered-Url, ".*\?", ""), "&oauth_signature=[^&]*", "");
	set req.http.X-OAuth-Base-String-Uri =
		if(req.http.Fastly-SSL, "https", "http")
		"://"
		std.tolower(req.http.host)
		req.url.path;
	set req.http.X-OAuth-Signature-Base-String =
		req.request
		"&"
		urlencode(req.http.X-OAuth-Base-String-Uri)
		"&"
		urlencode(req.http.X-OAuth-Parameters);

	set req.http.X-OAuth-Calculated-Signature = digest.hmac_sha1_base64(
			req.http.X-OAuth-Consumer-Secret "&" req.http.X-OAuth-Access-Token-Secret,
			req.http.X-OAuth-Signature-Base-String);

	unset req.http.X-OAuth-Consumer-Secret;
	unset req.http.X-OAuth-Access-Token-Secret;
	unset req.http.X-OAuth-Base-String-Uri;

	if(req.http.X-OAuth-Provided-Signature != req.http.X-OAuth-Calculated-Signature) {
		error 401 "Invalid OAuth Signature";
	}

	set req.http.X-OAuth-Timestamp = if(req.url ~ "(?i)oauth_timestamp=([0-9]*)", urldecode(re.group.1), "");
	if(req.http.X-OAuth-Timestamp == "") {
		error 401 "Missing/Invalid Timestamp";
	}

	if(time.is_after(
		now,
		time.add(std.integer2time(std.atoi(req.http.X-OAuth-Timestamp)), 30m))) {
		error 401 "Timestamp expired";
	}

	if(time.is_after(
		std.integer2time(std.atoi(req.http.X-OAuth-Timestamp)),
		time.add(now, 1m))) {
		error 401 "Timestamp too far in future";
	}

	error 200 "Authenticated!";

	return(lookup);
}

sub vcl_fetch {
#FASTLY fetch
	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
	# Expose variables for debugging
	set resp.http.X-OAuth-Consumer-Key = req.http.X-OAuth-Consumer-Key;
	set resp.http.X-OAuth-Access-Token = req.http.X-OAuth-Access-Token;
	set resp.http.X-OAuth-Provided-Signature = req.http.X-OAuth-Provided-Signature;
	set resp.http.X-OAuth-Calculated-Signature = req.http.X-OAuth-Calculated-Signature;
	set resp.http.X-OAuth-Parameters = req.http.X-OAuth-Parameters;
	set resp.http.X-OAuth-Signature-Base-String = req.http.X-OAuth-Signature-Base-String;
	return(deliver);
}

sub vcl_error {
#FASTLY error
#DEPLOY error
}

sub vcl_pass {
#FASTLY pass
}

table consumer_secrets {
	"foo": "super_secret"
}

table access_tokens {
	"bar": "token_secret"
}

sub vcl_recv {
#FASTLY recv
#DEPLOY recv
	set req.http.X-OAuth-Consumer-Key = if(req.url ~ "(?i)oauth_consumer_key=([^&]*)", urldecode(re.group.1), "");
	if(req.http.X-OAuth-Consumer-Key == "") {
		error 401 "Missing Consumer Key";
	}
	set req.http.X-OAuth-Consumer-Secret = table.lookup(consumer_secrets, req.http.X-OAuth-Consumer-Key);
	if(!req.http.X-OAuth-Consumer-Secret) {
		error 401 "Invalid Consumer Key";
	}
	set req.http.X-OAuth-Access-Token = if(req.url ~ "(?i)oauth_token=([^&]*)", urldecode(re.group.1), "");
	if(req.http.X-OAuth-Access-Token != "") {
		set req.http.X-OAuth-Access-Token-Secret = table.lookup(access_tokens, req.http.X-OAuth-Access-Token);
		if(!req.http.X-OAuth-Access-Token-Secret) {
			error 401 "Invalid Access Token";
		}
	} else {
		set req.http.X-OAuth-Access-Token-Secret = "";
	}
	set req.http.X-OAuth-Provided-Signature = if(req.url ~ "(?i)oauth_signature=([^&]*)", urldecode(re.group.1), "");
	if(req.http.X-OAuth-Provided-Signature == "") {
		error 401 "Missing Signature";
	}
	set req.http.X-OAuth-Ordered-Url = boltsort.sort(req.url);
	set req.http.X-OAuth-Parameters = regsub(regsub(req.http.X-OAuth-Ordered-Url, ".*\?", ""), "&oauth_signature=[^&]*", "");
	set req.http.X-OAuth-Base-String-Uri =
		if(req.http.Fastly-SSL, "https", "http")
		"://"
		std.tolower(req.http.host)
		req.url.path;
	set req.http.X-OAuth-Signature-Base-String =
		req.request
		"&"
		urlencode(req.http.X-OAuth-Base-String-Uri)
		"&"
		urlencode(req.http.X-OAuth-Parameters);

	set req.http.X-OAuth-Calculated-Signature = digest.hmac_sha1_base64(
			req.http.X-OAuth-Consumer-Secret "&" req.http.X-OAuth-Access-Token-Secret,
			req.http.X-OAuth-Signature-Base-String);

	unset req.http.X-OAuth-Consumer-Secret;
	unset req.http.X-OAuth-Access-Token-Secret;
	unset req.http.X-OAuth-Base-String-Uri;

	if(req.http.X-OAuth-Provided-Signature != req.http.X-OAuth-Calculated-Signature) {
		error 401 "Invalid OAuth Signature";
	}

	error 200 "Authenticated!";

	return(lookup);
}

sub vcl_fetch {
#FASTLY fetch
	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
	# Expose variables for debugging
	set resp.http.X-OAuth-Consumer-Key = req.http.X-OAuth-Consumer-Key;
	set resp.http.X-OAuth-Access-Token = req.http.X-OAuth-Access-Token;
	set resp.http.X-OAuth-Provided-Signature = req.http.X-OAuth-Provided-Signature;
	set resp.http.X-OAuth-Calculated-Signature = req.http.X-OAuth-Calculated-Signature;
	set resp.http.X-OAuth-Parameters = req.http.X-OAuth-Parameters;
	set resp.http.X-OAuth-Signature-Base-String = req.http.X-OAuth-Signature-Base-String;
	return(deliver);
}

sub vcl_error {
#FASTLY error
#DEPLOY error
}

sub vcl_pass {
#FASTLY pass
}


vcl, etc.

vcl, etc.

# This is an example VCL file for Varnish.
#
# It does not do anything by default, delegating control to the
# builtin VCL. The builtin VCL is called when there is no explicit
# return statement.
#
# See the VCL chapters in the Users Guide at https://www.varnish-cache.org/docs/
# and http://varnish-cache.org/trac/wiki/VCLExamples for more examples.

# Marker to tell the VCL compiler that this VCL has been adapted to the
# new 4.0 format.
vcl 4.0;

# https://github.com/Dridi/libvmod-querystring
import querystring;

# Default backend definition. Set this to point to your content server.
backend www {
	.host = "127.0.0.1";
	.port = "8080";
}

# Define an access control list to restrict cache purging.
acl purge {
	"127.0.0.1";
	"192.168.0.0"/16;
}

sub vcl_recv {

	# https://github.com/mattiasgeniar/varnish-4.0-configuration-templates/blob/master/default.vcl
	if (req.method == "PURGE") {
		if (!client.ip ~ purge) {
			return(synth(405, "Not allowed."));
		}
		return (purge);
	}

  if (req.method != "GET" &&
      req.method != "HEAD" &&
      req.method != "PUT" &&
      req.method != "POST" &&
      req.method != "TRACE" &&
      req.method != "OPTIONS" &&
      req.method != "PATCH" &&
      req.method != "DELETE") {
    return (pipe);
  }

  # Implementing websocket support (https://www.varnish-cache.org/docs/4.0/users-guide/vcl-example-websockets.html)
  if (req.http.Upgrade ~ "(?i)websocket") {
    return (pipe);
  }

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

  if (req.http.Authorization) {
    return (pass);
  }

	if (req.url~ "^/wp-admin/") {
		return (pass);
	}

	####

  if (req.url ~ "\#") {
    set req.url = regsub(req.url, "\#.*$", "");
  }

  if (req.url ~ "^[^?]*\.(bmp|bz2|css|doc|eot|flv|gif|gz|ico|jpeg|jpg|js|less|pdf|png|rtf|swf|txt|woff|xml)(\?.*)?$") {
    unset req.http.Cookie;
    set req.url = querystring.remove(req.url);
    return (hash);
  }

	set req.url = querystring.clean(req.url);

  if (req.url ~ "\?") {
    set req.url = querystring.sort(req.url);
  }

  return (hash);
}

sub vcl_hash {

  hash_data(req.url);

  if (req.http.host) {
    hash_data(req.http.host);
  } else {
    hash_data(server.ip);
  }

  if (req.http.origin) {
  	hash_data(req.http.origin);
  }

  return (lookup);
}

sub vcl_backend_response {
	# Happens after we have read the response headers from the backend.
	#
	# Here you clean the response headers, removing silly Set-Cookie headers
	# and other mistakes your backend does.
}

sub vcl_deliver {
	# Fall-through for dev that doesn't work right now. Prob need to do something with beresp too.
	# Happens when we have all the pieces we need, and are about to send the
	# response to the client.
	#
	# You can do accounting or modifying the final object here.
}

# This is an example VCL file for Varnish.
#
# It does not do anything by default, delegating control to the
# builtin VCL. The builtin VCL is called when there is no explicit
# return statement.
#
# See the VCL chapters in the Users Guide at https://www.varnish-cache.org/docs/
# and http://varnish-cache.org/trac/wiki/VCLExamples for more examples.

# Marker to tell the VCL compiler that this VCL has been adapted to the
# new 4.0 format.
vcl 4.0;

# https://github.com/Dridi/libvmod-querystring
import querystring;

# Default backend definition. Set this to point to your content server.
backend nytco {
	.host = "127.0.0.1";
	.port = "8080";
}

# Define an access control list to restrict cache purging.
acl purge {
	"192.168.33.1"/8;

}

sub vcl_recv {

	# Fall-through for admin interface requests
	if (req.url~ "^/wp-admin/") {
		return (pass);
	}

	# Purge the cache if the client is allowed to.
	if (req.method == "PURGE") {
		if (!client.ip ~ purge) {
			return(synth(405, "Not allowed."));
		}
		return (purge);
	}

	if (req.url ~ ".nytco.com$") {
			set req.url = querystring.remove(req.url);

	# Ignore all cookies.
	unset req.http.cookie;
	}
}

sub vcl_backend_response {
	# Happens after we have read the response headers from the backend.
	#
	# Here you clean the response headers, removing silly Set-Cookie headers
	# and other mistakes your backend does.
}

sub vcl_deliver {
	# Fall-through for dev that doesn't work right now. Prob need to do something with beresp too.
	set resp.http.age = "0";

	# Happens when we have all the pieces we need, and are about to send the
	# response to the client.
	#
	# You can do accounting or modifying the final object here.
}


Use req.url.qs for query string extraction; boltsort url by default

table consumer_secrets {
	"foo": "foo_secret"
}

table access_tokens {
	"bar": "bar_secret"
}

sub vcl_recv {
#FASTLY recv
#DEPLOY recv
	declare local var.consumer_key STRING;
	declare local var.consumer_secret STRING;
	declare local var.access_token STRING;
	declare local var.access_token_secret STRING;
	declare local var.provided_signature STRING;
	declare local var.parameters STRING;
	declare local var.base_string_uri STRING;
	declare local var.base_string STRING;
	declare local var.calculated_signature STRING;
	declare local var.timestamp STRING;

	set req.url = boltsort.sort(req.url);

	set var.consumer_key = if(req.url ~ "(?i)oauth_consumer_key=([^&]*)",
			urldecode(re.group.1), "");
	if(var.consumer_key == "") {
		error 401 "Missing Consumer Key";
	}
	set var.consumer_secret = table.lookup(consumer_secrets, var.consumer_key, "");
	if(var.consumer_secret == "") {
		error 401 "Invalid Consumer Key";
	}
	set var.access_token = if(req.url ~ "(?i)oauth_token=([^&]*)",
			urldecode(re.group.1), "");
	if(var.access_token != "") {
		set var.access_token_secret = table.lookup(access_tokens, var.access_token, "");
		if(var.access_token_secret == "") {
			error 401 "Invalid Access Token";
		}
	} else {
		set var.access_token_secret = "";
	}
	set var.provided_signature = if(req.url ~ "(?i)oauth_signature=([^&]*)",
			urldecode(re.group.1), "");
	if(var.provided_signature == "") {
		error 401 "Missing Signature";
	}
	set var.parameters = regsub(
			req.url.qs,
			"&oauth_signature=[^&]*", "");
	set var.base_string_uri =
		if(req.http.Fastly-SSL, "https", "http")
			"://"
				std.tolower(req.http.host)
				req.url.path;
	set var.base_string =
		req.request
		"&"
		urlencode(var.base_string_uri)
		"&"
		urlencode(var.parameters);

	set var.calculated_signature = digest.hmac_sha1_base64(
			var.consumer_secret "&" var.access_token_secret,
			var.base_string);

	if(!digest.secure_is_equal(var.provided_signature,
			var.calculated_signature)) {
		error 401 "Invalid OAuth Signature";
	}

	set var.timestamp = if(req.url ~ "(?i)oauth_timestamp=([0-9]*)",
			urldecode(re.group.1), "");
	if(var.timestamp == "") {
		error 401 "Missing/Invalid Timestamp";
	}

	if(time.is_after(
				now,
				time.add(std.integer2time(std.atoi(var.timestamp)), 30m))) {
		error 401 "Timestamp expired";
	}

	if(time.is_after(
				std.integer2time(std.atoi(var.timestamp)),
				time.add(now, 1m))) {
		error 401 "Timestamp too far in future";
	}

	error 200 "Authenticated!";

	return(lookup);
}

sub vcl_fetch {
#FASTLY fetch
	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
#DEPLOY error
}

sub vcl_pass {
#FASTLY pass
}

table consumer_secrets {
	"foo": "foo_secret"
}

table access_tokens {
	"bar": "bar_secret"
}

sub vcl_recv {
#FASTLY recv
#DEPLOY recv
	declare local var.consumer_key STRING;
	declare local var.consumer_secret STRING;
	declare local var.access_token STRING;
	declare local var.access_token_secret STRING;
	declare local var.provided_signature STRING;
	declare local var.parameters STRING;
	declare local var.base_string_uri STRING;
	declare local var.base_string STRING;
	declare local var.calculated_signature STRING;
	declare local var.timestamp STRING;

	set var.consumer_key = if(req.url ~ "(?i)oauth_consumer_key=([^&]*)",
			urldecode(re.group.1), "");
	if(var.consumer_key == "") {
		error 401 "Missing Consumer Key";
	}
	set var.consumer_secret = table.lookup(consumer_secrets, var.consumer_key, "");
	if(var.consumer_secret == "") {
		error 401 "Invalid Consumer Key";
	}
	set var.access_token = if(req.url ~ "(?i)oauth_token=([^&]*)",
			urldecode(re.group.1), "");
	if(var.access_token != "") {
		set var.access_token_secret = table.lookup(access_tokens, var.access_token, "");
		if(var.access_token_secret == "") {
			error 401 "Invalid Access Token";
		}
	} else {
		set var.access_token_secret = "";
	}
	set var.provided_signature = if(req.url ~ "(?i)oauth_signature=([^&]*)",
			urldecode(re.group.1), "");
	if(var.provided_signature == "") {
		error 401 "Missing Signature";
	}
	set var.parameters = regsub(
			regsub(boltsort.sort(req.url), ".*\?", ""),
			"&oauth_signature=[^&]*", "");
	set var.base_string_uri =
		if(req.http.Fastly-SSL, "https", "http")
			"://"
				std.tolower(req.http.host)
				req.url.path;
	set var.base_string =
		req.request
		"&"
		urlencode(var.base_string_uri)
		"&"
		urlencode(var.parameters);

	set var.calculated_signature = digest.hmac_sha1_base64(
			var.consumer_secret "&" var.access_token_secret,
			var.base_string);

	if(!digest.secure_is_equal(var.provided_signature,
			var.calculated_signature)) {
		error 401 "Invalid OAuth Signature";
	}

	set var.timestamp = if(req.url ~ "(?i)oauth_timestamp=([0-9]*)",
			urldecode(re.group.1), "");
	if(var.timestamp == "") {
		error 401 "Missing/Invalid Timestamp";
	}

	if(time.is_after(
				now,
				time.add(std.integer2time(std.atoi(var.timestamp)), 30m))) {
		error 401 "Timestamp expired";
	}

	if(time.is_after(
				std.integer2time(std.atoi(var.timestamp)),
				time.add(now, 1m))) {
		error 401 "Timestamp too far in future";
	}

	error 200 "Authenticated!";

	return(lookup);
}

sub vcl_fetch {
#FASTLY fetch
	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
#DEPLOY error
}

sub vcl_pass {
#FASTLY pass
}


add web socket upgrade piping

add web socket upgrade piping

# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.

import std;

# static
backend default {
  .host = "127.0.0.1";
  .port = "4000";
}
backend examples {
  .host = "127.0.0.1";
  .port = "8080";
}
backend components {
  .host = "127.0.0.1";
  .port = "3330";
}

sub vcl_recv {
  if (!req.http.Host) {
    error 404 "Need a host header";
  }

  set req.http.Host = regsub(req.http.Host, "^www\.", "");
  set req.http.Host = regsub(req.http.Host, ":80$", "");

  if (req.restarts == 0) {
    if (req.http.x-forwarded-for) {
      set req.http.X-Forwarded-For =
        req.http.X-Forwarded-For + ", " + client.ip;
    } else {
      set req.http.X-Forwarded-For = client.ip;
    }
  }

  if (
    req.http.Host ~ "^chat\." ||
    req.http.Host ~ "^charts\." ||
    req.http.Host ~ "^directory\." ||
    req.http.Host ~ "^codemirror\." ||
    req.http.Host ~ "^hello\." ||
    req.http.Host ~ "^sink\." ||
    req.http.Host ~ "^todos\." ||
    req.http.Host ~ "^widgets\."
  ) {
    set req.backend = examples;
  } else if (req.http.Host ~ "^components\.") {
    set req.backend = components;
  }

  if (req.request != "GET" &&
      req.request != "HEAD" &&
      req.request != "PUT" &&
      req.request != "POST" &&
      req.request != "TRACE" &&
      req.request != "OPTIONS" &&
      req.request != "DELETE") {
    /* Non-RFC2616 or CONNECT which is weird. */
    return (pipe);
  }
  if (req.http.Upgrade ~ "(?i)websocket") {
    return (pipe);
  }
  if (req.request != "GET" && req.request != "HEAD") {
    /* We only deal with GET and HEAD by default */
    return (pass);
  }
  if (req.http.Authorization || req.http.Cookie) {
    /* Not cacheable by default */
    return (pass);
  }
  return (lookup);
}

sub vcl_hash {
  hash_data(req.url);
  if (req.http.host) {
    hash_data(req.http.host);
  } else {
    hash_data(server.ip);
  }
  if (req.http.Origin) {
    hash_data(req.http.Origin);
  }
  return (hash);
}

sub vcl_pipe {
  # Note that only the first request to the backend will have
  # X-Forwarded-For set.  If you use X-Forwarded-For and want to
  # have it set for all requests, make sure to have:
  set bereq.http.connection = "close";
  # here.  It is not set by default as it might break some broken web
  # applications, like IIS with NTLM authentication.

  # Websockets
  if (req.http.upgrade) {
    set bereq.http.upgrade = req.http.upgrade;
  }

  return (pipe);
}

sub vcl_fetch {
  # Compress responses
  if (beresp.http.content-type ~ "text"
      || beresp.http.content-type ~ "json"
      || beresp.http.content-type ~ "javascript") {
    set beresp.do_gzip = true;
  }
}

sub vcl_error {
  if (obj.status == 750) {
    # moved permanently
    set obj.http.Location = req.http.Location;
    set obj.status = 301;
  } else if (obj.status == 752) {
    # moved temporarily
    set obj.http.Location = req.http.Location;
    set obj.status = 302;
  } else {
    set obj.http.Content-Type = "text/html; charset=utf-8";
    set obj.http.Retry-After = "5";
    synthetic std.fileread("/etc/varnish/503.html");
  }
  return (deliver);
}

# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.

import std;

# static
backend default {
  .host = "127.0.0.1";
  .port = "4000";
}
backend examples {
  .host = "127.0.0.1";
  .port = "8080";
}
backend components {
  .host = "127.0.0.1";
  .port = "3330";
}

sub vcl_recv {
  if (!req.http.Host) {
    error 404 "Need a host header";
  }

  set req.http.Host = regsub(req.http.Host, "^www\.", "");
  set req.http.Host = regsub(req.http.Host, ":80$", "");

  if (req.restarts == 0) {
    if (req.http.x-forwarded-for) {
      set req.http.X-Forwarded-For =
        req.http.X-Forwarded-For + ", " + client.ip;
    } else {
      set req.http.X-Forwarded-For = client.ip;
    }
  }

  if (
    req.http.Host ~ "^chat\." ||
    req.http.Host ~ "^charts\." ||
    req.http.Host ~ "^directory\." ||
    req.http.Host ~ "^codemirror\." ||
    req.http.Host ~ "^hello\." ||
    req.http.Host ~ "^sink\." ||
    req.http.Host ~ "^todos\." ||
    req.http.Host ~ "^widgets\."
  ) {
    set req.backend = examples;
  } else if (req.http.Host ~ "^components\.") {
    set req.backend = components;
  }

  if (req.request != "GET" &&
      req.request != "HEAD" &&
      req.request != "PUT" &&
      req.request != "POST" &&
      req.request != "TRACE" &&
      req.request != "OPTIONS" &&
      req.request != "DELETE") {
    /* Non-RFC2616 or CONNECT which is weird. */
    return (pipe);
  }
  if (req.request != "GET" && req.request != "HEAD") {
    /* We only deal with GET and HEAD by default */
    return (pass);
  }
  if (req.http.Authorization || req.http.Cookie) {
    /* Not cacheable by default */
    return (pass);
  }
  return (lookup);
}

sub vcl_hash {
  hash_data(req.url);
  if (req.http.host) {
    hash_data(req.http.host);
  } else {
    hash_data(server.ip);
  }
  if (req.http.Origin) {
    hash_data(req.http.Origin);
  }
  return (hash);
}

sub vcl_pipe {
  # Note that only the first request to the backend will have
  # X-Forwarded-For set.  If you use X-Forwarded-For and want to
  # have it set for all requests, make sure to have:
  set bereq.http.connection = "close";
  # here.  It is not set by default as it might break some broken web
  # applications, like IIS with NTLM authentication.

  # Websockets
  if (req.http.upgrade) {
    set bereq.http.upgrade = req.http.upgrade;
  }

  return (pipe);
}

sub vcl_fetch {
  # Compress responses
  if (beresp.http.content-type ~ "text"
      || beresp.http.content-type ~ "json"
      || beresp.http.content-type ~ "javascript") {
    set beresp.do_gzip = true;
  }
}

sub vcl_error {
  if (obj.status == 750) {
    # moved permanently
    set obj.http.Location = req.http.Location;
    set obj.status = 301;
  } else if (obj.status == 752) {
    # moved temporarily
    set obj.http.Location = req.http.Location;
    set obj.status = 302;
  } else {
    set obj.http.Content-Type = "text/html; charset=utf-8";
    set obj.http.Retry-After = "5";
    synthetic std.fileread("/etc/varnish/503.html");
  }
  return (deliver);
}


Only append country_code if it doesn't exist and also allow geo override for testing

###############################################################################
#
# Fastly CDN for Magento 2
#
# NOTICE OF LICENSE
#
# This source file is subject to the Fastly CDN for Magento 2 End User License
# Agreement that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
#
# @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
# @license     BSD, see LICENSE_FASTLY_CDN.txt
#
###############################################################################

# This is a basic VCL configuration file for Fastly CDN for Magento 2 module.

sub vcl_recv {
#FASTLY recv

    # 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;
    }

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

    if (req.url ~ "^/static/version(\d*/)?(.*)$") {
       set req.url = "/static/" + re.group.2 + "?" + re.group.1;
    }

    # auth for purging
    if (req.request == "FASTLYPURGE") {
        # extract token signature and expiration
        set req.http.X-Sig = regsub(req.http.X-Purge-Token, "^[^_]+_(.*)", "\1");
        set req.http.X-Exp = regsub(req.http.X-Purge-Token, "^([^_]+)_.*", "\1");

        # validate signature
        if (req.http.X-Sig == regsub(digest.hmac_sha1(req.service_id, req.url.path req.http.X-Exp), "^0x", "")) {

            # use vcl time math to check expiration timestamp
            set req.http.X-Original-Grace = req.grace;
            set req.grace = std.atoi(strftime({"%s"}, now));
            set req.grace -= std.atoi(req.http.X-Exp);

            if (std.atoi(req.grace) > 0) {
                error 410;
            }

            # clean up grace since we used it for time math
            set req.grace = std.atoi(req.http.X-Original-Grace);
            unset req.http.X-Original-Grace;

        } else {
            error 403;
        }

        # cleanup variables
        unset req.http.X-Purge-Token;
        unset req.http.X-Sig;
        unset req.http.X-Exp;
    }

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

    # disable ESI processing on Origin Shield
    if (req.http.Fastly-FF) {
        set req.esi = false;
    }

    # We only deal with GET and HEAD by default
    if (req.request != "GET" && req.request != "HEAD") {
        return (pass);
    }

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

    # Bypass shopping cart and checkout requests
    if (req.url ~ "/checkout") {
        return (pass);
    }

    # geoip lookup
    if (req.url ~ "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|form_key)=") {
            error 200 "";
        } else {
            # append parameter with country code only if it doesn't exist already
            if ( req.url !~ "country_code=" ) {
                set req.url = req.url "?country_code=" if ( req.http.geo_override, req.http.geo_override, geoip.country_code);
            }
        }
    }

    return(lookup);
}

sub vcl_fetch {

    # 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;
        }

    }

#FASTLY fetch

    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;
        # 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.http.Pragma = "no-cache";
            set beresp.http.Expires = "-1";
            set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
            set beresp.grace = 1m;
        }

        # 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);
    }

    return (deliver);
}

sub vcl_hit {
#FASTLY hit

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

sub vcl_miss {
    # Deactivate gzip on origin
    unset bereq.http.Accept-Encoding;

#FASTLY miss

    return(fetch);
}

sub vcl_deliver {

    # Add an easy way to see whether custom Fastly VCL has been uploaded
    if ( req.http.Fastly-Debug ) {
        set resp.http.Fastly-Magento-VCL-Uploaded = "yes";
    } else {
        remove resp.http.Fastly-Module-Enabled;
    }
    # debug info
    if (resp.http.X-Magento-Debug) {
        if (obj.hits > 0) {
            set resp.http.X-Magento-Cache-Debug = "HIT";
            set resp.http.X-Magento-Cache-Hits = obj.hits;
        } else {
            set resp.http.X-Magento-Cache-Debug = "MISS";
        }
    } else {
        # remove Varnish/proxy header
        remove resp.http.Age;
        remove resp.http.X-Magento-Debug;
        remove resp.http.X-Magento-Tags;
        remove resp.http.X-Surrogate-Key;
        remove resp.http.X-Magento-Cache-Control;
        remove resp.http.X-Powered-By;
        remove resp.http.Server;
        remove resp.http.X-Varnish;
        remove resp.http.Via;
        remove resp.http.Link;
        remove resp.http.X-Purge-URL;
        remove resp.http.X-Purge-Host;
    }

#FASTLY deliver

    return (deliver);
}

sub vcl_error {
    # workaround for possible security issue
    if (req.url ~ "^\s") {
        set obj.status = 400;
        set obj.response = "Malformed request";
        synthetic "";
        return (deliver);
    }

    # error 200
    if (obj.status == 200) {
        return (deliver);
    }

     set obj.http.Content-Type = "text/html; charset=utf-8";
     set obj.http.Retry-After = "5";
     synthetic {"
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

        <title>"} obj.status " " obj.response {"</title>

Error "} obj.status " " obj.response {"

"} obj.response {"

Guru Meditation:

XID: "} req.xid {"

Fastly CDN server

"};

#FASTLY error
}

sub vcl_pass {
#FASTLY pass
}

sub vcl_hash {
    set req.hash += req.http.Https;
    set req.hash += req.http.host;
    set req.hash += req.url;

    if (req.http.cookie:X-Magento-Vary ) {
        set req.hash += req.http.cookie:X-Magento-Vary;
    }

    set req.hash += "#####GENERATION#####";

    ### {{ design_exceptions_code }} ###

    return (hash);
}

###############################################################################
#
# Fastly CDN for Magento 2
#
# NOTICE OF LICENSE
#
# This source file is subject to the Fastly CDN for Magento 2 End User License
# Agreement that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
#
# @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
# @license     BSD, see LICENSE_FASTLY_CDN.txt
#
###############################################################################

# This is a basic VCL configuration file for Fastly CDN for Magento 2 module.

sub vcl_recv {
#FASTLY recv

    # 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;
    }

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

    if (req.url ~ "^/static/version(\d*/)?(.*)$") {
       set req.url = "/static/" + re.group.2 + "?" + re.group.1;
    }

    # auth for purging
    if (req.request == "FASTLYPURGE") {
        # extract token signature and expiration
        set req.http.X-Sig = regsub(req.http.X-Purge-Token, "^[^_]+_(.*)", "\1");
        set req.http.X-Exp = regsub(req.http.X-Purge-Token, "^([^_]+)_.*", "\1");

        # validate signature
        if (req.http.X-Sig == regsub(digest.hmac_sha1(req.service_id, req.url.path req.http.X-Exp), "^0x", "")) {

            # use vcl time math to check expiration timestamp
            set req.http.X-Original-Grace = req.grace;
            set req.grace = std.atoi(strftime({"%s"}, now));
            set req.grace -= std.atoi(req.http.X-Exp);

            if (std.atoi(req.grace) > 0) {
                error 410;
            }

            # clean up grace since we used it for time math
            set req.grace = std.atoi(req.http.X-Original-Grace);
            unset req.http.X-Original-Grace;

        } else {
            error 403;
        }

        # cleanup variables
        unset req.http.X-Purge-Token;
        unset req.http.X-Sig;
        unset req.http.X-Exp;
    }

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

    # disable ESI processing on Origin Shield
    if (req.http.Fastly-FF) {
        set req.esi = false;
    }

    # We only deal with GET and HEAD by default
    if (req.request != "GET" && req.request != "HEAD") {
        return (pass);
    }

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

    # Bypass shopping cart and checkout requests
    if (req.url ~ "/checkout") {
        return (pass);
    }

    # geoip lookup
    if (req.url ~ "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|form_key)=") {
            error 200 "";
        } else {
            # append parameter with country code
            set req.url = req.url "?country_code=" geoip.country_code;
        }
    }

    return(lookup);
}

sub vcl_fetch {

    # 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;
        }

    }

#FASTLY fetch

    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;
        # 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.http.Pragma = "no-cache";
            set beresp.http.Expires = "-1";
            set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
            set beresp.grace = 1m;
        }

        # 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);
    }

    return (deliver);
}

sub vcl_hit {
#FASTLY hit

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

sub vcl_miss {
    # Deactivate gzip on origin
    unset bereq.http.Accept-Encoding;

#FASTLY miss

    return(fetch);
}

sub vcl_deliver {

    # Add an easy way to see whether custom Fastly VCL has been uploaded
    if ( req.http.Fastly-Debug ) {
        set resp.http.Fastly-Magento-VCL-Uploaded = "yes";
    } else {
        remove resp.http.Fastly-Module-Enabled;
    }
    # debug info
    if (resp.http.X-Magento-Debug) {
        if (obj.hits > 0) {
            set resp.http.X-Magento-Cache-Debug = "HIT";
            set resp.http.X-Magento-Cache-Hits = obj.hits;
        } else {
            set resp.http.X-Magento-Cache-Debug = "MISS";
        }
    } else {
        # remove Varnish/proxy header
        remove resp.http.Age;
        remove resp.http.X-Magento-Debug;
        remove resp.http.X-Magento-Tags;
        remove resp.http.X-Surrogate-Key;
        remove resp.http.X-Magento-Cache-Control;
        remove resp.http.X-Powered-By;
        remove resp.http.Server;
        remove resp.http.X-Varnish;
        remove resp.http.Via;
        remove resp.http.Link;
        remove resp.http.X-Purge-URL;
        remove resp.http.X-Purge-Host;
    }

#FASTLY deliver

    return (deliver);
}

sub vcl_error {
    # workaround for possible security issue
    if (req.url ~ "^\s") {
        set obj.status = 400;
        set obj.response = "Malformed request";
        synthetic "";
        return (deliver);
    }

    # error 200
    if (obj.status == 200) {
        return (deliver);
    }

     set obj.http.Content-Type = "text/html; charset=utf-8";
     set obj.http.Retry-After = "5";
     synthetic {"
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

        <title>"} obj.status " " obj.response {"</title>

Error "} obj.status " " obj.response {"

"} obj.response {"

Guru Meditation:

XID: "} req.xid {"

Fastly CDN server

"};

#FASTLY error
}

sub vcl_pass {
#FASTLY pass
}

sub vcl_hash {
    set req.hash += req.http.Https;
    set req.hash += req.http.host;
    set req.hash += req.url;

    if (req.http.cookie:X-Magento-Vary ) {
        set req.hash += req.http.cookie:X-Magento-Vary;
    }

    set req.hash += "#####GENERATION#####";

    ### {{ design_exceptions_code }} ###

    return (hash);
}


Set the age header to 0 before sending the request back to the browser (#1282)

* Remove the age header before sending the request back to the browser

* Set Age header to 0 when delivering response

import boltsort;

sub vcl_recv {
#FASTLY recv

	# Enable API key authentication for URL purge requests
	if ( req.request == "FASTLYPURGE" ) {
		set req.http.Fastly-Purge-Requires-Auth = "1";
	}

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

	if (req.http.Host ~ "polyfills.io") {
		# Do the canonicalise check before the SSL check to avoid a double redirect
		error 751 "Canonicalise";
	}

	if (!req.http.Fastly-SSL) {
		# 801 is a special error code that Fastly uses to Force SSL on the request
		error 801 "Redirect to prod HTTPS";
	}

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

	if (req.url ~ "^/v2/recordRumData" && req.http.Normalized-User-Agent) {
		set req.http.Log = regsub(req.url, "^.*?\?(.*)$", "\1") "&ip=" client.ip "&refer_domain=" regsub(req.http.Referer, "^(https?\:\/\/)?(www\.)?(.+?)(\:\d+)?([\/\?].*)?$", "\3") "&country=" geoip.country_code "&data_center=" if(req.http.Cookie:FastlyDC, req.http.Cookie:FastlyDC, server.datacenter);
		error 204 "No Content";
	}

	set req.url = boltsort.sort(req.url);

	if (req.restarts == 0) {
		set req.http.X-Original-Host = req.http.Host;

		if (req.http.Host == "qa.polyfill.io") {
			if (req.http.X-Geoip-Continent ~ "(NA|SA|OC|AS)") {
				set req.backend = origami_polyfill_service_us;
				set req.http.Host = "ft-polyfill-service-us-qa.herokuapp.com";

				if (!req.backend.healthy) {
					set req.backend = origami_polyfill_service_eu;
					set req.http.Host = "ft-polyfill-service-qa.herokuapp.com";
				}

			} else {
				set req.backend = origami_polyfill_service_eu;
				set req.http.Host = "ft-polyfill-service-qa.herokuapp.com";

				if (!req.backend.healthy) {
					set req.backend = origami_polyfill_service_us;
					set req.http.Host = "ft-polyfill-service-us-qa.herokuapp.com";
				}
			}
		} else {
			if (req.http.X-Geoip-Continent ~ "(NA|SA|OC|AS)") {
				set req.backend = origami_polyfill_service_us;
				set req.http.Host = "ft-polyfill-service-us.herokuapp.com";

				if (!req.backend.healthy) {
					set req.backend = origami_polyfill_service_eu;
					set req.http.Host = "ft-polyfill-service.herokuapp.com";
				}

			} else {
				set req.backend = origami_polyfill_service_eu;
				set req.http.Host = "ft-polyfill-service.herokuapp.com";

				if (!req.backend.healthy) {
					set req.backend = origami_polyfill_service_us;
					set req.http.Host = "ft-polyfill-service-us.herokuapp.com";
				}
			}
		}
	}

	# https://community.fastly.com/t/brotli-compression-support/578/6
	if (req.http.Fastly-Orig-Accept-Encoding) {
		if (req.http.User-Agent ~ "MSIE 6") {
			# For that 0.3% of stubborn users out there
			unset req.http.Accept-Encoding;
		} elsif (req.http.Fastly-Orig-Accept-Encoding ~ "br") {
			set req.http.Accept-Encoding = "br";
		} elsif (req.http.Fastly-Orig-Accept-Encoding ~ "gzip") {
			set req.http.Accept-Encoding = "gzip";
		} else {
			unset req.http.Accept-Encoding;
		}
	}

	return(lookup);
}

sub vcl_deliver {
#FASTLY deliver

	# If the response is to a normalise request and there's a parked "original request", use the normalised UA response to modify the original request and restart it
	if (req.url ~ "^/v\d/normalizeUa" && resp.status == 200 && req.http.X-Orig-URL ~ "^/v2/(polyfill\.|recordRumData)") {
		set req.http.Fastly-force-Shield = "1";
		set req.http.Normalized-User-Agent = resp.http.Normalized-User-Agent;
		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;

	# If the original request didn't specify a UA override, and we added one when the request was sent to the backend, the backend won't have included a Vary:user-agent header (correctly) but we need to add one
	} else if (req.url ~ "^/v\d/polyfill\..*[\?\&]ua=" && req.http.X-Orig-URL && req.http.X-Orig-URL !~ "[\?\&]ua=") {
		add resp.http.Vary = "User-Agent";
	}

	if (req.url ~ "[\&\?]rum=1") {
		add resp.http.Set-Cookie = "FastlyDC=" server.datacenter "; Path=/; HttpOnly; max-age=60";
	}

	if (req.http.Fastly-Debug) {
		set resp.http.Debug-Host = req.http.Host;
		set resp.http.Debug-X-Original-Host = req.http.X-Original-Host;
	}

	set resp.http.Age = 0;

	return(deliver);
}

sub vcl_error {
#FASTLY error

	# Redirect to canonical prod/qa origins
	if (obj.status == 751) {
		set obj.status = 301;
		set obj.response = "Moved Permanently";
		set obj.http.Location = "https://polyfill.io" req.url;
		synthetic {""};
		return (deliver);
	}
}

import boltsort;

sub vcl_recv {
#FASTLY recv

	# Enable API key authentication for URL purge requests
	if ( req.request == "FASTLYPURGE" ) {
		set req.http.Fastly-Purge-Requires-Auth = "1";
	}

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

	if (req.http.Host ~ "polyfills.io") {
		# Do the canonicalise check before the SSL check to avoid a double redirect
		error 751 "Canonicalise";
	}

	if (!req.http.Fastly-SSL) {
		# 801 is a special error code that Fastly uses to Force SSL on the request
		error 801 "Redirect to prod HTTPS";
	}

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

	if (req.url ~ "^/v2/recordRumData" && req.http.Normalized-User-Agent) {
		set req.http.Log = regsub(req.url, "^.*?\?(.*)$", "\1") "&ip=" client.ip "&refer_domain=" regsub(req.http.Referer, "^(https?\:\/\/)?(www\.)?(.+?)(\:\d+)?([\/\?].*)?$", "\3") "&country=" geoip.country_code "&data_center=" if(req.http.Cookie:FastlyDC, req.http.Cookie:FastlyDC, server.datacenter);
		error 204 "No Content";
	}

	set req.url = boltsort.sort(req.url);

	if (req.restarts == 0) {
		set req.http.X-Original-Host = req.http.Host;

		if (req.http.Host == "qa.polyfill.io") {
			if (req.http.X-Geoip-Continent ~ "(NA|SA|OC|AS)") {
				set req.backend = origami_polyfill_service_us;
				set req.http.Host = "ft-polyfill-service-us-qa.herokuapp.com";

				if (!req.backend.healthy) {
					set req.backend = origami_polyfill_service_eu;
					set req.http.Host = "ft-polyfill-service-qa.herokuapp.com";
				}

			} else {
				set req.backend = origami_polyfill_service_eu;
				set req.http.Host = "ft-polyfill-service-qa.herokuapp.com";

				if (!req.backend.healthy) {
					set req.backend = origami_polyfill_service_us;
					set req.http.Host = "ft-polyfill-service-us-qa.herokuapp.com";
				}
			}
		} else {
			if (req.http.X-Geoip-Continent ~ "(NA|SA|OC|AS)") {
				set req.backend = origami_polyfill_service_us;
				set req.http.Host = "ft-polyfill-service-us.herokuapp.com";

				if (!req.backend.healthy) {
					set req.backend = origami_polyfill_service_eu;
					set req.http.Host = "ft-polyfill-service.herokuapp.com";
				}

			} else {
				set req.backend = origami_polyfill_service_eu;
				set req.http.Host = "ft-polyfill-service.herokuapp.com";

				if (!req.backend.healthy) {
					set req.backend = origami_polyfill_service_us;
					set req.http.Host = "ft-polyfill-service-us.herokuapp.com";
				}
			}
		}
	}

	# https://community.fastly.com/t/brotli-compression-support/578/6
	if (req.http.Fastly-Orig-Accept-Encoding) {
		if (req.http.User-Agent ~ "MSIE 6") {
			# For that 0.3% of stubborn users out there
			unset req.http.Accept-Encoding;
		} elsif (req.http.Fastly-Orig-Accept-Encoding ~ "br") {
			set req.http.Accept-Encoding = "br";
		} elsif (req.http.Fastly-Orig-Accept-Encoding ~ "gzip") {
			set req.http.Accept-Encoding = "gzip";
		} else {
			unset req.http.Accept-Encoding;
		}
	}

	return(lookup);
}

sub vcl_deliver {
#FASTLY deliver

	# If the response is to a normalise request and there's a parked "original request", use the normalised UA response to modify the original request and restart it
	if (req.url ~ "^/v\d/normalizeUa" && resp.status == 200 && req.http.X-Orig-URL ~ "^/v2/(polyfill\.|recordRumData)") {
		set req.http.Fastly-force-Shield = "1";
		set req.http.Normalized-User-Agent = resp.http.Normalized-User-Agent;
		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;

	# If the original request didn't specify a UA override, and we added one when the request was sent to the backend, the backend won't have included a Vary:user-agent header (correctly) but we need to add one
	} else if (req.url ~ "^/v\d/polyfill\..*[\?\&]ua=" && req.http.X-Orig-URL && req.http.X-Orig-URL !~ "[\?\&]ua=") {
		add resp.http.Vary = "User-Agent";
	}

	if (req.url ~ "[\&\?]rum=1") {
		add resp.http.Set-Cookie = "FastlyDC=" server.datacenter "; Path=/; HttpOnly; max-age=60";
	}

	if (req.http.Fastly-Debug) {
		set resp.http.Debug-Host = req.http.Host;
		set resp.http.Debug-X-Original-Host = req.http.X-Original-Host;
	}

	return(deliver);
}

sub vcl_error {
#FASTLY error

	# Redirect to canonical prod/qa origins
	if (obj.status == 751) {
		set obj.status = 301;
		set obj.response = "Moved Permanently";
		set obj.http.Location = "https://polyfill.io" req.url;
		synthetic {""};
		return (deliver);
	}
}


Integration mode description

Integration mode description

# This is a the VCL configuration file for 3scale's varnish plugin.
#
# Default backend definition.  Set this to point to the 3scale's
# backend.
#
# set the beresp.ttl in vcl_fetch to have a default global TTL
# you can define custom TTL via regular expressions
#
# Integration mode: stand-alone out of band system.
# Traffic from API consumers will not go through Varnish
# (see https://support.3scale.net/howtos/api-configuration/varnish)

backend default {
  .host = "su1.3scale.net";
  .port = "80";
}

import std;
import threescale;

sub vcl_recv {

	unset req.http.cookie;
	set req.http.host = "su1.3scale.net";
	set req.grace = 1s;

	if (req.request != "GET" && req.request != "HEAD") {
      		return(pass);
    	}
    	else {
      		## GET or HEAD requests
      		return(lookup);
    	}

}

# the hash of the request needs to be customized to remove all fields that are not
# associated to the user

sub vcl_hash {

	## remove no_body
	set req.http.X-url-tmp = regsub(req.url,"[&?]no_body.[^&]*","");
	## remove object_id
	set req.http.X-url-tmp = regsub(req.http.X-url-tmp,"[&?]object.[^&]*","");
	## remove object
	set req.http.X-url-tmp = regsub(req.http.X-url-tmp,"[&?]object_id.[^&]*","");

	hash_data(req.http.X-url-tmp);
	unset req.http.X-url-tmp;
     	return (hash);

}

#A HIT means that the request to your local cache of 3scale is still fresh and the
#response will not have to fetch the data from the remote 3scale backend.
#vmod_3scale plugin will send a replica of the call asynchronously to 3scale's remote
#backend on its own

sub vcl_hit {

  if (req.url ~ "^/transactions/authrep.xml\?") {
	  if (req.url ~ "^/transactions/authrep.xml\?") {
	    if (threescale.send_get_request_threaded("su1.3scale.net","80",req.url,"")==0) {}
    }
  }

}

sub vcl_fetch {
	set beresp.ttl = 30s;
}

# This is a the VCL configuration file for 3scale's varnish plugin.
#
# Default backend definition.  Set this to point to the 3scale's
# backend.
#
# set the beresp.ttl in vcl_fetch to have a default global TTL
# you can define custom TTL via regular expressions
#

backend default {
  .host = "su1.3scale.net";
  .port = "80";
}

import std;
import threescale;

sub vcl_recv {

	unset req.http.cookie;
	set req.http.host = "su1.3scale.net";
	set req.grace = 1s;

	if (req.request != "GET" && req.request != "HEAD") {
      		return(pass);
    	}
    	else {
      		## GET or HEAD requests
      		return(lookup);
    	}

}

# the hash of the request needs to be customized to remove all fields that are not
# associated to the user

sub vcl_hash {

	## remove no_body
	set req.http.X-url-tmp = regsub(req.url,"[&?]no_body.[^&]*","");
	## remove object_id
	set req.http.X-url-tmp = regsub(req.http.X-url-tmp,"[&?]object.[^&]*","");
	## remove object
	set req.http.X-url-tmp = regsub(req.http.X-url-tmp,"[&?]object_id.[^&]*","");

	hash_data(req.http.X-url-tmp);
	unset req.http.X-url-tmp;
     	return (hash);

}

#A HIT means that the request to your local cache of 3scale is still fresh and the
#response will not have to fetch the data from the remote 3scale backend.
#vmod_3scale plugin will send a replica of the call asynchronously to 3scale's remote
#backend on its own

sub vcl_hit {

  if (req.url ~ "^/transactions/authrep.xml\?") {
	  if (req.url ~ "^/transactions/authrep.xml\?") {
	    if (threescale.send_get_request_threaded("su1.3scale.net","80",req.url,"")==0) {}
    }
  }

}

sub vcl_fetch {
	set beresp.ttl = 30s;
}


Fix typo in examples/example.vcl (redis.call -> redis.send)

Signed-off-by: Noah Williamsson <01479f9bf7817090e07c55bace445c48178b77b4@gmail.com>

#
# A trivial example to demonstrate how to use this vmod
#

import redis;

backend be1 {
  .host = "192.168.0.1";
  .port = "80";
}

#sub vcl_init {
  #
  # By default, the redis module will attempt to connect to a Redis server
  # at 127.0.0.1:6379 with a connect timeout of 200 milliseconds.
  #
  # The function redis.init_redis(host, port, timeout_ms) may be used to
  # connect to an alternate Redis server or use a different connect timeout.
  #
  # redis.init_redis("localhost", 6379, 200);  /* default values */
#}

sub vcl_recv {
  #
  # redis.send is a procedure, it will send the command to redis and ignore
  # the response. If the command errors out, it will be logged but the VCL
  # will not know.
  #
  redis.send("LPUSH client " + client.ip);

  #
  # redis.call is a function that sends the command to redis and return the
  # return value as a string.
  #
  set req.http.x-redis = redis.call("LTRIM client 0 99");
}

#
# A trivial example to demonstrate how to use this vmod
#

import redis;

backend be1 {
  .host = "192.168.0.1";
  .port = "80";
}

#sub vcl_init {
  #
  # By default, the redis module will attempt to connect to a Redis server
  # at 127.0.0.1:6379 with a connect timeout of 200 milliseconds.
  #
  # The function redis.init_redis(host, port, timeout_ms) may be used to
  # connect to an alternate Redis server or use a different connect timeout.
  #
  # redis.init_redis("localhost", 6379, 200);  /* default values */
#}

sub vcl_recv {
  #
  # redis.call is a procedure, it will send the command to redis and ignore
  # the response. If the command errors out, it will be logged but the VCL
  # will not know.
  #
  redis.send("LPUSH client " + client.ip);

  #
  # redis.call is a function that sends the command to redis and return the
  # return value as a string.
  #
  set req.http.x-redis = redis.call("LTRIM client 0 99");
}


Add detection for windows mobile

Add detection for windows mobile

sub devicedetect {
    unset req.http.X-UA-Device-Type;

    // sensible defaults
    set req.http.X-UA-Device-Type = "generic";
    set req.http.X-UA-Device-OS   = "unknown";

    if (req.http.Cookie ~ "(?i)X-UA-Device-Force") {
    } else {
        if (req.http.User-Agent ~ "(?i)ipad") {
            set req.http.X-UA-Device-Type = "tablet";
            set req.http.X-UA-Device-OS   = "ios";
        } elsif (req.http.User-Agent ~ "(?i)ip(hone|od)") {
            set req.http.X-UA-Device-Type = "mobile";
            set req.http.X-UA-Device-OS   = "ios";
        } elsif (req.http.User-Agent ~ "(?i)android") {
            // http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent
            set req.http.X-UA-Device-OS = "android";
            if (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") {
                set req.http.X-UA-Device-Type = "mobile";
            } elseif (req.http.User-Agent ~ "(?i)android 3") { // looks like android 3/honeycomb are mostly tablet-only
                set req.http.X-UA-Device-Type = "tablet";
            } elseif (req.http.User-Agent ~ "(?i)android 2") { // it should not exist any tablet prior to android 3
                set req.http.X-UA-Device-Type = "mobile";
            } else {
                // fallback on tablet
                set req.http.X-UA-Device-Type = "tablet";
            }
        } elseif (req.http.User-Agent ~ "(?i)windows phone" || req.http.User-Agent ~ "(?i)windowsphone") { // Should we match IEMobile too or instead?
            set req.http.X-UA-Device-Type = "mobile";
            set req.http.X-UA-Device-OS   = "windowsphone";
        } elseif (req.http.User-Agent ~ "(?i)windows mobile" || req.http.User-Agent ~ "(?i)windowsmobile") { // Not sure if it makes sense
            set req.http.X-UA-Device-Type = "mobile";
            set req.http.X-UA-Device-OS   = "windowsmobile";
        } elseif (req.http.User-Agent ~ "(?i)blackberry[0-9]?" || req.http.User-Agent ~ "(?i)BB10") {
            set req.http.X-UA-Device-OS   = "blackberry";
            // For now, let's assume all blackberry devices are mobile
            // Afaik, it does not exist blackberry tablets
            set req.http.X-UA-Device-Type = "mobile";
        }
    }
}

sub devicedetect {
    unset req.http.X-UA-Device-Type;

    // sensible defaults
    set req.http.X-UA-Device-Type = "generic";
    set req.http.X-UA-Device-OS   = "unknown";

    if (req.http.Cookie ~ "(?i)X-UA-Device-Force") {
    } else {
        if (req.http.User-Agent ~ "(?i)ipad") {
            set req.http.X-UA-Device-Type = "tablet";
            set req.http.X-UA-Device-OS   = "ios";
        } elsif (req.http.User-Agent ~ "(?i)ip(hone|od)") {
            set req.http.X-UA-Device-Type = "mobile";
            set req.http.X-UA-Device-OS   = "ios";
        } elsif (req.http.User-Agent ~ "(?i)android") {
            // http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent
            set req.http.X-UA-Device-OS = "android";
            if (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") {
                set req.http.X-UA-Device-Type = "mobile";
            } elseif (req.http.User-Agent ~ "(?i)android 3") { // looks like android 3/honeycomb are mostly tablet-only
                set req.http.X-UA-Device-Type = "tablet";
            } elseif (req.http.User-Agent ~ "(?i)android 2") { // it should not exist any tablet prior to android 3
                set req.http.X-UA-Device-Type = "mobile";
            } else {
                // fallback on tablet
                set req.http.X-UA-Device-Type = "tablet";
            }
        } elseif (req.http.User-Agent ~ "(?i)windows phone" || req.http.User-Agent ~ "(?i)windowsphone") { // Should we match IEMobile too or instead?
            set req.http.X-UA-Device-Type = "mobile";
            set req.http.X-UA-Device-OS   = "windowsphone";
        } elseif (req.http.User-Agent ~ "(?i)blackberry[0-9]?" || req.http.User-Agent ~ "(?i)BB10") {
            set req.http.X-UA-Device-OS   = "blackberry";
            // For now, let's assume all blackberry devices are mobile
            // Afaik, it does not exist blackberry tablets
            set req.http.X-UA-Device-Type = "mobile";
        }
    }
}


Add copyright and license information to devicedetect.vcl

# Copyright (c) 2012-2013 Varnish Software AS
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# detectdevice.vcl - regex based device detection for Varnish
# http://github.com/varnish/varnish-devicedetect/
#
# Author: Lasse Karstensen <lasse@varnish-software.com>

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

	# Handle that a cookie may override the detection alltogether.
	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");
		/* Clean up our mess in the cookie header */
		set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *X-UA-Device-force=[^;]+;? *", "\1");
		/* If the cookie header is now empty, or just whitespace, unset it. */
		if (req.http.Cookie ~ "^ *$") { unset req.http.Cookie; }
	} else {
		if (req.http.User-Agent ~ "(?i)(ads|google|bing|msn|yandex|baidu|ro|career|)bot" ||
		    req.http.User-Agent ~ "(?i)(baidu|jike|symantec)spider" ||
		    req.http.User-Agent ~ "(?i)scanner" ||
		    req.http.User-Agent ~ "(?i)(web)crawler") {
			set req.http.X-UA-Device = "bot"; }
		elsif (req.http.User-Agent ~ "(?i)ipad")        { set req.http.X-UA-Device = "tablet-ipad"; }
		elsif (req.http.User-Agent ~ "(?i)ip(hone|od)") { set req.http.X-UA-Device = "mobile-iphone"; }
		/* 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"; }
		// android 3/honeycomb was just about tablet-only, and any phones will probably handle a bigger page layout.
		elsif (req.http.User-Agent ~ "(?i)android 3")              { set req.http.X-UA-Device = "tablet-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 ~ "Mobile.+Firefox")     { set req.http.X-UA-Device = "mobile-firefoxos"; }
		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";
		}
	}
}


Reindent with 4 spaces, 2 for continuations

/*-
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2014 Varnish Software AS
 * All rights reserved.
 *
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *

 *
 * The built-in (previously called default) VCL code.
 *
 * NB! You do NOT need to copy & paste all of these functions into your
 * own vcl code, if you do not provide a definition of one of these
 * functions, the compiler will automatically fall back to the default
 * code from this file.
 *
 * This code will be prefixed with a backend declaration built from the
 * -b argument.
 */

vcl 4.0;

#######################################################################
# Client side

sub vcl_recv {
    if (req.restarts == 0) {
        if (req.http.x-forwarded-for) {
            set req.http.X-Forwarded-For =
                req.http.X-Forwarded-For + ", " + client.ip;
        } else {
            set req.http.X-Forwarded-For = client.ip;
        }
    }
    if (req.method != "GET" &&
      req.method != "HEAD" &&
      req.method != "PUT" &&
      req.method != "POST" &&
      req.method != "TRACE" &&
      req.method != "OPTIONS" &&
      req.method != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        return (pipe);
    }
    if (req.method != "GET" && req.method != "HEAD") {
        /* We only deal with GET and HEAD by default */
        return (pass);
    }
    if (req.http.Authorization || req.http.Cookie) {
        /* Not cacheable by default */
        return (pass);
    }
    return (hash);
}

sub vcl_pipe {
    # Note that only the first request to the backend will have
    # X-Forwarded-For set.  If you use X-Forwarded-For and want to
    # have it set for all requests, make sure to have:
    # set bereq.http.connection = "close";
    # here.  It is not set by default as it might break some broken web
    # applications, like IIS with NTLM authentication.
    return (pipe);
}

sub vcl_pass {
    return (fetch);
}

sub vcl_hash {
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }
    return (lookup);
}

sub vcl_purge {
    return (error(200, "Purged"));
}

sub vcl_hit {
    if (obj.ttl >= 0s) {
        // A pure unadultered hit, deliver it
        return (deliver);
    }
    if (obj.ttl + obj.grace > 0s) {
        // Object is in grace, deliver it
        // Automatically triggers a background fetch
        return (deliver);
    }
    // fetch & deliver once we get the result
    return (fetch);
}

sub vcl_miss {
    return (fetch);
}

sub vcl_deliver {
    /*
     * These two write to the stored object causing extra page faults
     * Enable them only if you need them.
     *
     * set obj.hits = obj.hits + 1;
     * set obj.last_use = now;
     */
    return (deliver);
}

/*
 * We can come here "invisibly" with the following errors: 413, 417 & 503
 */
sub vcl_error {
    set obj.http.Content-Type = "text/html; charset=utf-8";
    set obj.http.Retry-After = "5";
    synthetic {"
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

    <title>"} + obj.status + " " + obj.reason + {"</title>

Error "} + obj.status + " " + obj.reason + {"

"} + obj.reason + {"

Guru Meditation:

XID: "} + req.xid + {"

Varnish cache server

"};
    return (deliver);
}

#######################################################################
# Backend Fetch

sub vcl_backend_fetch {
    return (fetch);
}

sub vcl_backend_response {
    if (beresp.ttl <= 0s ||
      beresp.http.Set-Cookie ||
      beresp.http.Surrogate-control ~ "no-store" ||
      (!beresp.http.Surrogate-Control &&
        beresp.http.Cache-Control ~ "no-cache|no-store|private") ||
      beresp.http.Vary == "*") {
        /*
        * Mark as "Hit-For-Pass" for the next 2 minutes
        */
        set beresp.ttl = 120s;
        set beresp.uncacheable = true;
    }
    return (deliver);
}

sub vcl_backend_error {
    return (deliver);
}

#######################################################################
# Housekeeping

sub vcl_init {
    return (ok);
}

sub vcl_fini {
    return (ok);
}


Apply IO header on both shield and edge

Apply IO header on both shield and edge

if ( req.url.ext ~ "(?i)^(gif|png|jpg|jpeg|webp)$" ) {

  set req.http.X-Fastly-Imageopto-Api = "fastly";

  if (req.url.qs != "") {
    set req.url = req.url.path "?" req.url.qs "&auto=webp";
  } else {
    set req.url = req.url.path "?auto=webp";
  }
}

if (!req.http.Fastly-FF && req.url.ext ~ "(?i)^(gif|png|jpg|jpeg|webp)$") {

  set req.http.X-Fastly-Imageopto-Api = "fastly";

  if (req.url.qs != "") {
    set req.url = req.url.path "?" req.url.qs "&auto=webp";
  } else {
    set req.url = req.url.path "?auto=webp";
  }
}


Remove escaped slashes as they are not necessary in regexes. Avoids the new VCL linter from complaining

    /* 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);
        }
    }

    # Remove Set-Cookies from responses for static content to match the cookie removal in recv.
    if (req.http.x-long-cache || 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;
            set beresp.http.Cache-Control = "max-age=0";
        } else if (req.http.x-long-cache) {
            # Force caching for signed cached assets.
            set beresp.ttl = 31536000s;
            # Add immutable as it avoids IMS and INM revalidations
            set beresp.http.Cache-Control = "max-age=31536000, immutable";
        }
    }

    # Fix Vary Header in some cases. In 99.9% of cases Varying on User-Agent is counterproductive
    # https://www.varnish-cache.org/trac/wiki/VCLExampleFixupVary
    if (beresp.http.Vary ~ "User-Agent") {
        unset beresp.http.Vary:User-Agent;
        if (beresp.http.Vary == "") {
            unset beresp.http.Vary;
        }
    }

    # All the Magento responses should emit X-Esi headers
    if (beresp.http.x-esi) {
        # enable ESI feature for Magento response by default
        esi;
    } else {
        # enable gzip for all static content except
        if ( http_status_matches(beresp.status, "200,404") && (beresp.http.content-type ~ "^(application/x\-javascript|text/css|text/html|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.ext ~ "(?i)(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") {
                set beresp.http.Vary:Accept-Encoding = "";
            }
            if (req.http.Accept-Encoding == "gzip") {
                set beresp.gzip = true;
            }
        }
    }

    # Add Varying on X-Magento-Vary
    if (beresp.http.Content-Type ~ "text/(html|xml)" || req.http.graphql) {
        set beresp.http.Vary:X-Magento-Vary = "";
        set beresp.http.Vary:Https = "";
    }

    # Just in case the Request Setting for x-pass is missing
    if (req.http.x-pass) {
        return (pass);
    }

    if (beresp.http.Cache-Control ~ "private|no-cache|no-store") {
        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;
    }

    # Never cache 302s
    if (beresp.status == 302) {
        return (pass);
    }

    if (beresp.http.x-amz-request-id) {
        # If assets are coming from Amazon they may have no Cache-Control headers which may make them uncacheable
    } else if (!beresp.http.Expires && !beresp.http.Surrogate-Control ~ "max-age" && !beresp.http.Cache-Control ~ "(s-maxage|max-age)") {
        # Varnish sets default TTL if none of the headers above are present. If not set we want to make sure we don't cache it
        set beresp.ttl = 0s;
        set beresp.cacheable = false;
    }

    # 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") && !req.http.x-pass ) {
        unset beresp.http.set-cookie;

        # 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 they are image/script or CSS
        if (beresp.http.Content-Type ~ "(image|script|css)") {
            set beresp.http.Surrogate-Key = re.group.1;
        }
    }

    /* 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);
        }
    }

    # Remove Set-Cookies from responses for static content to match the cookie removal in recv.
    if (req.http.x-long-cache || 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;
            set beresp.http.Cache-Control = "max-age=0";
        } else if (req.http.x-long-cache) {
            # Force caching for signed cached assets.
            set beresp.ttl = 31536000s;
            # Add immutable as it avoids IMS and INM revalidations
            set beresp.http.Cache-Control = "max-age=31536000, immutable";
        }
    }

    # Fix Vary Header in some cases. In 99.9% of cases Varying on User-Agent is counterproductive
    # https://www.varnish-cache.org/trac/wiki/VCLExampleFixupVary
    if (beresp.http.Vary ~ "User-Agent") {
        unset beresp.http.Vary:User-Agent;
        if (beresp.http.Vary == "") {
            unset beresp.http.Vary;
        }
    }

    # All the Magento responses should emit X-Esi headers
    if (beresp.http.x-esi) {
        # enable ESI feature for Magento response by default
        esi;
    } else {
        # enable gzip for all static content except
        if ( http_status_matches(beresp.status, "200,404") && (beresp.http.content-type ~ "^(application\/x\-javascript|text\/css|text\/html|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.ext ~ "(?i)(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") {
                set beresp.http.Vary:Accept-Encoding = "";
            }
            if (req.http.Accept-Encoding == "gzip") {
                set beresp.gzip = true;
            }
        }
    }

    # Add Varying on X-Magento-Vary
    if (beresp.http.Content-Type ~ "text/(html|xml)" || req.http.graphql) {
        set beresp.http.Vary:X-Magento-Vary = "";
        set beresp.http.Vary:Https = "";
    }

    # Just in case the Request Setting for x-pass is missing
    if (req.http.x-pass) {
        return (pass);
    }

    if (beresp.http.Cache-Control ~ "private|no-cache|no-store") {
        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;
    }

    # Never cache 302s
    if (beresp.status == 302) {
        return (pass);
    }

    if (beresp.http.x-amz-request-id) {
        # If assets are coming from Amazon they may have no Cache-Control headers which may make them uncacheable
    } else if (!beresp.http.Expires && !beresp.http.Surrogate-Control ~ "max-age" && !beresp.http.Cache-Control ~ "(s-maxage|max-age)") {
        # Varnish sets default TTL if none of the headers above are present. If not set we want to make sure we don't cache it
        set beresp.ttl = 0s;
        set beresp.cacheable = false;
    }

    # 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") && !req.http.x-pass ) {
        unset beresp.http.set-cookie;

        # 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 they are image/script or CSS
        if (beresp.http.Content-Type ~ "(image|script|css)") {
            set beresp.http.Surrogate-Key = re.group.1;
        }
    }


re #120 - Exclude /installation folder from Varnish caching

vcl 4.0;

# This Varnish configuration is a very basic template to get started with caching Joomla sites.
# In no way is this configuration complete: every site is unique and needs customisation!
#
# Joomla creates a session cookie by default, even if you not logged in. To deal with this,
# we need to let Varnish know when a user is logged in or not. If the user is not logged in,
# we will prevent the Joomla response from setting any cookies so Varnish can cache the page.
#
# To set the X-Logged-In header, append the following line in to the onAfterInitialise() method
# in /plugins/system/cache/cache.php, right after "$user = JFactory::getUser();" (line #60):
#
# JFactory::getApplication()->setHeader('X-Logged-In', $user->guest ? 'false' : 'true', true);
#
# Now enable the Cache plugin and Varnish cache. Clear your existing cookies.
# Your front-end pages will be cached as long as you browse the site as a guest.
#
# This VCL is based on https://snipt.net/fevangelou/the-perfect-varnish-configuration-for-joomla-websites/

import std;

backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .probe = {
      .url = "/varnish-enabled";
      .interval = 1s;
      .timeout = 1s;
    }
}

backend alternative {
    .host = "127.0.0.1";
    .port = "8080";
}

sub vcl_recv {
        # Forward client's IP to backend
        unset req.http.X-Forwarded-For;
        set req.http.X-Forwarded-For = client.ip;

        set req.http.X-Forwarded-By = server.ip;
        set req.http.X-Forwarded-Port = 80;

        # Check if we've still enabled Varnish, if not, passthrough every request
        if (! std.healthy(req.backend_hint))
        {
            set req.backend_hint = alternative;
            return (pass);
        }

        # Do not cache phpmyadmin
        if (req.http.host == "phpmyadmin.joomla.box") {
            return (pass);
        }

        # Do not cache system tools on joomla.box:
        if (req.http.host == "joomla.box")
        {
            if (req.url == "/apc" || req.url == "/phpinfo" || req.url == "/pimpmylog") {
                return (pass);
            }
        }

        # Do not cache POST requests
        if (req.method == "POST") {
            return (pass);
        }

        # Proxy (pass) any request that goes to the backend admin, the banner component links, ..
        if(req.url ~ "/administrator" || req.url ~ "/component/banners" || req.url ~ "/component/users" || req.url ~ "/installation") {
            return (pass);
        }

        # Do not cache if user is logged in
        if (req.http.Authorization || req.http.Authenticate || req.http.Cookie) {
            return (pass);
        }

        # Don't cache ajax requests
        if(req.http.X-Requested-With == "XMLHttpRequest" || req.url ~ "nocache") {
            return (pass);
        }

        # Properly handle different encoding types
        if (req.http.Accept-Encoding) {
          if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf)$") {
            # No point in compressing these
            unset req.http.Accept-Encoding;
          } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
          } elsif (req.http.Accept-Encoding ~ "deflate") {
            set req.http.Accept-Encoding = "deflate";
          } else {
            # unknown algorithm (aka crappy browser)
            unset req.http.Accept-Encoding;
          }
        }

        return (hash);
}

sub vcl_backend_response {
        # Unset the "etag" header (suggested)
        unset beresp.http.etag;

        # This is Joomla! specific: fix stupid "no-cache" header sent by Joomla! even
        # when caching is on - make sure to replace 300 with the number of seconds that
        # you want the browser to cache content
        if(beresp.http.Cache-Control ~ "no-cache" || beresp.http.Cache-Control == ""){
            set beresp.http.Cache-Control = "max-age=300, public, must-revalidate";
        }

        # Check for the custom "x-Logged-In" header to identify if the visitor is a guest,
        # then unset any cookie (including session cookies) provided it's not a POST request.
        if(bereq.method != "POST" && beresp.http.X-Logged-In == "false") {
            unset beresp.http.Set-Cookie;
        }

        # This is how long Varnish will cache content
        set beresp.ttl = 1w;

        return (deliver);
}

sub vcl_deliver {
        if (resp.http.x-varnish ~ " ") {
            set resp.http.X-Varnish-Status = "HIT";
        } else {
            set resp.http.X-Varnish-Status = "MISS";
        }

        # Please note that obj.hits behaviour changed in 4.0, now it counts per objecthead, not per object
        # and obj.hits may not be reset in some cases where bans are in use. See bug 1492 for details.
        # So take hits with a grain of salt
        set resp.http.X-Varnish-Hits = obj.hits;
}

vcl 4.0;

# This Varnish configuration is a very basic template to get started with caching Joomla sites.
# In no way is this configuration complete: every site is unique and needs customisation!
#
# Joomla creates a session cookie by default, even if you not logged in. To deal with this,
# we need to let Varnish know when a user is logged in or not. If the user is not logged in,
# we will prevent the Joomla response from setting any cookies so Varnish can cache the page.
#
# To set the X-Logged-In header, append the following line in to the onAfterInitialise() method
# in /plugins/system/cache/cache.php, right after "$user = JFactory::getUser();" (line #60):
#
# JFactory::getApplication()->setHeader('X-Logged-In', $user->guest ? 'false' : 'true', true);
#
# Now enable the Cache plugin and Varnish cache. Clear your existing cookies.
# Your front-end pages will be cached as long as you browse the site as a guest.
#
# This VCL is based on https://snipt.net/fevangelou/the-perfect-varnish-configuration-for-joomla-websites/

import std;

backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .probe = {
      .url = "/varnish-enabled";
      .interval = 1s;
      .timeout = 1s;
    }
}

backend alternative {
    .host = "127.0.0.1";
    .port = "8080";
}

sub vcl_recv {
        # Forward client's IP to backend
        unset req.http.X-Forwarded-For;
        set req.http.X-Forwarded-For = client.ip;

        set req.http.X-Forwarded-By = server.ip;
        set req.http.X-Forwarded-Port = 80;

        # Check if we've still enabled Varnish, if not, passthrough every request
        if (! std.healthy(req.backend_hint))
        {
            set req.backend_hint = alternative;
            return (pass);
        }

        # Do not cache phpmyadmin
        if (req.http.host == "phpmyadmin.joomla.box") {
            return (pass);
        }

        # Do not cache system tools on joomla.box:
        if (req.http.host == "joomla.box")
        {
            if (req.url == "/apc" || req.url == "/phpinfo" || req.url == "/pimpmylog") {
                return (pass);
            }
        }

        # Proxy (pass) any request that goes to the backend admin,
        # the banner component links or any post requests
        if(req.url ~ "/administrator" || req.url ~ "/component/banners" || req.url ~ "/component/users" || req.method == "POST") {
            return (pass);
        }

        # Do not cache if user is logged in
        if (req.http.Authorization || req.http.Authenticate || req.http.Cookie) {
            return (pass);
        }

        # Don't cache ajax requests
        if(req.http.X-Requested-With == "XMLHttpRequest" || req.url ~ "nocache") {
            return (pass);
        }

        # Properly handle different encoding types
        if (req.http.Accept-Encoding) {
          if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf)$") {
            # No point in compressing these
            unset req.http.Accept-Encoding;
          } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
          } elsif (req.http.Accept-Encoding ~ "deflate") {
            set req.http.Accept-Encoding = "deflate";
          } else {
            # unknown algorithm (aka crappy browser)
            unset req.http.Accept-Encoding;
          }
        }

        return (hash);
}

sub vcl_backend_response {
        # Unset the "etag" header (suggested)
        unset beresp.http.etag;

        # This is Joomla! specific: fix stupid "no-cache" header sent by Joomla! even
        # when caching is on - make sure to replace 300 with the number of seconds that
        # you want the browser to cache content
        if(beresp.http.Cache-Control ~ "no-cache" || beresp.http.Cache-Control == ""){
            set beresp.http.Cache-Control = "max-age=300, public, must-revalidate";
        }

        # Check for the custom "x-Logged-In" header to identify if the visitor is a guest,
        # then unset any cookie (including session cookies) provided it's not a POST request.
        if(bereq.method != "POST" && beresp.http.X-Logged-In == "false") {
            unset beresp.http.Set-Cookie;
        }

        # This is how long Varnish will cache content
        set beresp.ttl = 1w;

        return (deliver);
}

sub vcl_deliver {
        if (resp.http.x-varnish ~ " ") {
            set resp.http.X-Varnish-Status = "HIT";
        } else {
            set resp.http.X-Varnish-Status = "MISS";
        }

        # Please note that obj.hits behaviour changed in 4.0, now it counts per objecthead, not per object
        # and obj.hits may not be reset in some cases where bans are in use. See bug 1492 for details.
        # So take hits with a grain of salt
        set resp.http.X-Varnish-Hits = obj.hits;
}


Delete excel.vcl

Delete excel.vcl

# Voice commands for excel

include numbers.vch;

#data warehouse commands
The Data Warehouse = "SQL5SV";
Connect to (Data Warehouse | It) = {Alt}a x Wait(100) d {Enter} Wait(100) {Enter};
Go There = {F6}  Wait(100) {Tab};

#Timesheet commands
Find Anders = {Ctrl+f} Wait(100) "Anders" {Enter} {Esc};
Normal Timesheet = "X" {Tab} Wait(100) "X" {Tab} Wait(100) "X" {Tab} Wait(100) "X" {Tab} Wait(100) "X" {Tab};

Grab Down <numbers> = {Shift+Down_$1};
Fill Down <numbers> = {Shift+Down_$1}  {Alt}h fi d;

Bottom = {Ctrl+Down};
Top = {Ctrl+Up} {Down};
(Poo-Owm | Homa) = {Tab} {Home};

Print Preview = {Alt+f}w v;

Latest File = {Alt}f r 1;
(List Files | File List) = {Alt}f r;

[Set] Landscape = {Alt} p o Wait(100) {Down} {Enter};

Visible Cells = {Alt+e}g Wait(100) {Alt+s}{Alt+y} Wait(100) {Enter};
Paste Special ={Alt+e} st {Enter};
Hide Column = {Ctrl+0};
Hide 1..15 Columns = {Ctrl+space} {Shift+Right_$1}{Shift+left}{Ctrl+0};
Show Columns = {Alt+o}cu;

Line = {Alt+e}dr {Enter};

Clean Paste = {Alt+e} st {Enter};

<numbers> (Left | Right | Up | Down) = {$2_$1};

Fill (Down='d' | Right = 'r') = {Ctrl+$1};

Grab <numbers> Columns = {Ctrl+space} {Shift+Right_$1} {Shift+left};
Delete <numbers> Columns = {Ctrl+space} {Shift+Right_$1} {Shift+left} {Alt}h dc;
Row Height = {Alt+o} re;
Column Width = {Alt}h o w;

Sort = {Alt+d} s;
Auto Filter = {Alt+d} f f;
Subtotals = {Alt+d} b;
Insert (Row = 'r' | Column = 'c' | Sheet = 'w') = {Alt+i} $1;

(Autoformat | Autoformat Column ) = {Alt+o}ca;
Format (Column = 'w' | Row = 'h') Width = {Alt} h o $1;
Format (Cells | Cell) = {Alt+o}e;
Cell Borders = {Alt} h b;
Format Number = {Alt+o}e Wait(100) {Alt+c}n {Alt+d}0  Wait(100) {Alt+u}{Enter};
Format (General = '~' | Date = '#' | Percent = '%') = {Ctrl+Shift+$1};

Insert Page Break = {Alt+i}b;

Set (Page | Print) Area = {Alt} p r s;

NexSheet = {Ctrl+PgDn};
LassSheet = {Ctrl+PgUp};

Rename (sheet | worksheet) = {Alt+o}hr;
Insert (Sheet | Worksheet) = {SHIFT+F11};

# Macro development commands
[Start] (Visual Basic | VBA) = {Alt+F11};
Run It = {Ctrl+Shift+l};

Microsoft Visual Basic for Applications:
	Reload = {Alt+q} Wait(100) {Ctrl+Shift+l};
	(Exit Code | Close Editor) = {Alt+q};
	Code Window = {F7};
	Object Browser = {F2};
	Project Window = {Ctrl+r};
	Immediate Window = {Ctrl+g};
	Next Window = {Ctrl+tab};
	Last Window = {Ctrl+Shift+Tab};

	Find  <_anything> = {Ctrl+f} $1 {Enter} Wait(100) {Esc};
	Find Again = {F3};
	Next Procedure = {Ctrl+Down};
	Last Procedure = {Ctrl+Up};

	Run = {F5};
	Run Step = {F8};
	Stop Running = {Ctrl+break};
	(Set | Clear) Breakpoint = {F9};
	Clear All Breakpoints = {Shift+F9};

	(shortcut | Shortcuts) Menu = {Shift+F10};

	#VBA commands
	New Variable = "Dim As Variant"  Wait(100) {Home} {Right_4};		# NOTE: Variant = any data type
	(Dim | Define) as (Variant | Object | Boolean | Integer | Long | Currency | Date | String) = "Dim  As " $2 Wait(100) {Home} {Right_4};
	MessageBox = 'MsgBox ';
	Range Value = 'Range("").Value' {Left_8};

#{Alt+F5} runs the error handling code or returns the error to the calling procedure
#{Alt+F8} steps into the error handler returns error to the calling procedure


Provide a commented out example of redis.init_redis() in examples/example.vcl

Signed-off-by: Noah Williamsson <01479f9bf7817090e07c55bace445c48178b77b4@gmail.com>

#
# A trivial example to demonstrate how to use this vmod
#

import redis;

backend be1 {
  .host = "192.168.0.1";
  .port = "80";
}

#sub vcl_init {
  #
  # By default, the redis module will attempt to connect to a Redis server
  # at 127.0.0.1:6379 with a connect timeout of 200 milliseconds.
  #
  # The function redis.init_redis(host, port, timeout_ms) may be used to
  # connect to an alternate Redis server or use a different connect timeout.
  #
  # redis.init_redis("localhost", 6379, 200);  /* default values */
#}

sub vcl_recv {
  #
  # redis.call is a procedure, it will send the command to redis and ignore
  # the response. If the command errors out, it will be logged but the VCL
  # will not know.
  #
  redis.send("LPUSH client " + client.ip);

  #
  # redis.call is a function that sends the command to redis and return the
  # return value as a string.
  #
  set req.http.x-redis = redis.call("LTRIM client 0 99");
}

#
# A trivial example to demonstrate how to use this vmod
#

import redis;

backend be1 {
  .host = "192.168.0.1";
  .port = "80";
}

sub vcl_recv {
  #
  # redis.call is a procedure, it will send the command to redis and ignore
  # the response. If the command errors out, it will be logged but the VCL
  # will not know.
  #
  redis.send("LPUSH client " + client.ip);

  #
  # redis.call is a function that sends the command to redis and return the
  # return value as a string.
  #
  set req.http.x-redis = redis.call("LTRIM client 0 99");
}