#65: Make backend port configurable in kodekit.vcl

vcl 4.0;
# Based on: https://github.com/mattiasgeniar/varnish-4.0-configuration-templates/blob/master/default.vcl

import std;
import directors;

probe health
{
    #.url = "/varnish-enabled";
    # We prefer to only do a HEAD /
    .request =
        "HEAD /varnish-enabled HTTP/1.1"
        "Host: localhost"
        "Connection: close";
        "User-Agent: Varnish Health Probe";
    .interval = 5s;   # check the health of each backend every 5 seconds
    .timeout = 1s;    # timing out after 1 second.
    # If 3 out of the last 5 polls succeeded the backend is considered healthy, otherwise it will be marked as sick
    .window = 5;
    .threshold = 3;
    .expected_response = 200;
}

backend default
{
    .host = "127.0.0.1";        # IP or Hostname of backend
    .port = "%1$s";             # Port Apache or whatever is listening
    .max_connections = 300;     # That's it
    .probe = health;
    .connect_timeout            = 600s;  # How long to wait before we receive a first byte from our backend?
    .first_byte_timeout         = 600s;  # How long to wait for a backend connection?
    .between_bytes_timeout      = 600s;  # How long to wait between bytes received from our backend?
}

backend passthrough
{
    .host = "127.0.0.1";        # IP or Hostname of backend
    .port = "%1$s";             # Port Apache or whatever is listening
}

acl localhost
{
    "localhost";
    "33.33.33.63";
    "::1";
}

sub vcl_init
{
    # Called when VCL is loaded, before any requests pass through it.
    # Typically used to initialize VMODs.

    new vdir = directors.round_robin();
    vdir.add_backend(default);
    # vdir.add_backend(server...);
    # vdir.add_backend(servern);
}

sub vcl_recv
{
    # Called at the beginning of a request, after the complete request has been received and parsed.
    # Its purpose is to decide whether or not to serve the request, how to do it, and, if applicable,
    # which backend to use and if needed to modify the request.

    # Send all traffic to the vdir director
    set req.backend_hint = vdir.backend();

    # Normalize the header, remove the port (in case you're testing this on various TCP ports)
    set req.http.Host = regsub(req.http.Host, ":[0-9]+", "");

    # Normalize the query arguments
    set req.url = std.querysort(req.url);

    # Allow purging
    if (req.method == "PURGE")
    {
        # purge is the ACL defined at the begining
        if (!client.ip ~ localhost) {
            return (synth(405, "This IP is not allowed to send PURGE requests."));
        }

        # If you got this stage (and didn't error out above), purge the cached result
        return (purge);
    }

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

    # Only deal with "normal" types
    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") {
        /* Non-RFC2616 or CONNECT which is weird. */
        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);
    }

    # Only cache GET or HEAD requests. This makes sure the POST requests are always passed.
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }

    # Specific Nooku Platform rules

        # Do not cache /administrator
        if(req.url ~ "^/administrator") {
            return (pass);
        }

    # Specific Nooku Server rules

        # Do not cache webgrind.nooku.dev
        if (req.http.host == "webgrind.nooku.dev") {
            return (pass);
        }

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

        # Do not cache /apc and /phpinfo
        if (req.url == "/apc" || req.url == "/phpinfo") {
            return (pass);
        }

    # Generic URL manipulation, useful for all templates that follow

        # Remove the Google Analytics added parameters, useless for our backend
        if (req.url ~ "(\?|&)(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=") {
            set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "");
            set req.url = regsuball(req.url, "\?(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "?");
            set req.url = regsub(req.url, "\?&", "?");
            set req.url = regsub(req.url, "\?$", "");
        }

        # Strip hash, server doesn't need it.
        if (req.url ~ "\#") {
            set req.url = regsub(req.url, "\#.*$", "");
        }

        # Strip a trailing ? if it exists
        if (req.url ~ "\?$") {
            set req.url = regsub(req.url, "\?$", "");
        }

    # Cookie manipulation

        # Remove the "has_js" cookie
        set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", "");

        # Remove any Google Analytics based cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "_gat=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmctr=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmcmd.=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmccn.=[^;]+(; )?", "");

        # Remove DoubleClick offensive cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "__gads=[^;]+(; )?", "");

        # Remove the Quant Capital cookies (added by some plugin, all __qca)
        set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", "");

        # Remove the AddThis cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "__atuv.=[^;]+(; )?", "");

        # Remove a ";" prefix in the cookie if present
        set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");

        # Remove the csrf_token cookie
        set req.http.Cookie = regsuball(req.http.Cookie, "csrf_token=[^;]+(; )?", "");

        # Are there cookies left with only spaces or that are empty?
        if (req.http.cookie ~ "^\s*$") {
            unset req.http.cookie;
        }

        # Large static files are delivered directly to the end-user without waiting for Varnish to fully read the file first.
        # Varnish 4 supports Streaming, so set do_stream in vcl_backend_response()
        if (req.url ~ "^[^?]*\.(7z|avi|bz2|flac|flv|gz|mka|mkv|mov|mp3|mp4|mpeg|mpg|ogg|ogm|opus|rar|tar|tgz|tbz|txz|wav|webm|xz|zip)(\?.*)?$") {
            unset req.http.Cookie;
            return (hash);
        }

        # Remove all cookies for static files
        # Before you blindly enable this read: https://ma.ttias.be/stop-caching-static-files/
        #if (req.method == "GET")
        #{
        #    # Cache files with these extensions and remove cookie
        #    if (req.url ~ "^[^?]*\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|otf|ogg|ogm|opus|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)(\?.*)?$") {
        #        unset req.http.Cookie;
        #        return (hash);
        #    }
        #}

    if (req.http.Cache-Control ~ "(?i)no-cache" && client.ip ~ localhost)
    {
        # http://varnish.projects.linpro.no/wiki/VCLExampleEnableForceRefresh
        # Ignore requests via proxy caches and badly behaved crawlers
        # like msnbot that send no-cache with every request.
        if (! (req.http.Via || req.http.User-Agent ~ "(?i)bot" || req.http.X-Purge))
        {
            # Doesn't seems to refresh the object in the cache
            set req.hash_always_miss = true;

            # Couple this with restart in vcl_purge and X-Purge header to avoid loops
            return(purge);
        }
    }

    # Send Surrogate-Capability headers to announce ESI support to backend
    set req.http.Surrogate-Capability = "varnish=ESI/1.0";

    # Requests that require authorization are not cacheable by default
    if (req.http.Authorization) {
        return (pass);
    }

    # We do not support SPDY or HTTP/2.0
    if (req.method == "PRI") {
        return (synth(405));
    }

    return (hash);
}

# Called upon entering pipe mode. In this mode, the request is passed on to the backend, and any further data from both
# the client and backend is passed on unaltered until either end closes the connection. Basically, Varnish will degrade
# into a simple TCP proxy, shuffling bytes back and forth. For a connection in pipe mode, no other VCL subroutine will
# ever get called after vcl_pipe.
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";
    # It is not set by default as it might break some broken web applications, like IIS with NTLM authentication.
    # set bereq.http.Connection = "Close";

    # Implementing websocket support (https://www.varnish-cache.org/docs/4.0/users-guide/vcl-example-websockets.html)
    if (req.http.upgrade) {
        set bereq.http.upgrade = req.http.upgrade;
    }

    return (pipe);
}

# Called upon entering pass mode. In this mode, the request is passed on to the backend, and the backend's response
# is passed on to the client, but is not entered into the cache. Subsequent requests submitted over the same client
# connection are handled normally.
sub vcl_pass
{
    return (fetch);
}

# Called after vcl_recv to create a hash value for the request. This is used as a key to look up the object in Varnish.
sub vcl_hash
{
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

    # hash cookies for requests that have them
    if (req.http.Cookie) {
        hash_data(req.http.Cookie);
    }
}

# Called when a cache lookup is successful.
sub vcl_hit
{
    # A pure unadultered hit, deliver it
    if (obj.ttl >= 0s) {
        return (deliver);
    }

    # https://www.varnish-cache.org/docs/trunk/users-guide/vcl-grace.html
    # When several clients are requesting the same page Varnish will send one request to the backend and place the
    # others on hold while fetching one copy from the backend. In some products this is called request coalescing and
    # Varnish does this automatically.

    # If you are serving thousands of hits per second the queue of waiting requests can get huge. There are two
    # potential problems - one is a thundering herd problem - suddenly releasing a thousand threads to serve content
    # might send the load sky high. Secondly - nobody likes to wait. To deal with this we can instruct Varnish to keep
    # the objects in cache beyond their TTL and to serve the waiting requests somewhat stale content.

    # if (!std.healthy(req.backend_hint) && (obj.ttl + obj.grace > 0s)) {
    #   return (deliver);
    # } else {
    #   return (fetch);
    # }

    # We have no fresh fish. Lets look at the stale ones.
    if (std.healthy(req.backend_hint))
    {
        # Backend is healthy. Limit age to 10s.
        if (obj.ttl + 10s > 0s)
        {
            #set req.http.grace = "normal(limited)";
            return (deliver);
        }
        else
        {
            # No candidate for grace. Fetch a fresh object.
            return(fetch);
        }
    }
    else
    {
        # backend is sick - use full grace
        if (obj.ttl + obj.grace > 0s)
        {
            #set req.http.grace = "full";
            return (deliver);
        }
        else
        {
            # no graced object.
            return (fetch);
        }
    }

    # fetch & deliver once we get the result
    return (fetch); # Dead code, keep as a safeguard
}

# Called after a cache lookup if the requested document was not found in the cache. Its purpose is to decide whether
# or not to attempt to retrieve the document from the backend, and which backend to use.
sub vcl_miss
{
    return (fetch);
}

 # Called after the response headers has been successfully retrieved from the backend.
sub vcl_backend_response
{
    # Enable ESI handling
    if (beresp.http.Surrogate-Control ~ "ESI/1.0" ) {
        set beresp.do_esi = true;
    }

    # Store the csrf_token cookie temporarily if the response is cacheabale
    if (beresp.http.Set-Cookie && !beresp.uncacheable)
    {
        set beresp.http.X-Varnish-Cookie = beresp.http.Set-Cookie;
        unset beresp.http.Set-Cookie;
    }

    # Cache handling all static files
    if (bereq.method == "GET")
    {
        # Before you blindly enable this, read: https://ma.ttias.be/stop-caching-static-files/
        #if (bereq.url ~ "^[^?]*\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|otf|ogg|ogm|opus|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)(\?.*)?$") {
        #    unset beresp.http.Set-Cookie;
        #}

        # Large static files are delivered directly to the end-user without waiting for Varnish to fully read the
        # file first. Check memory usage it'll grow in fetch_chunksize blocks (128k by default) if the backend
        # doesn't send a Content-Length header. Only enable it for large files
        if (bereq.url ~ "^[^?]*\.(7z|avi|bz2|flac|flv|gz|mka|mkv|mov|mp3|mp4|mpeg|mpg|ogg|ogm|opus|rar|tar|tgz|tbz|txz|wav|webm|xz|zip)(\?.*)?$") {
        {
            unset beresp.http.Set-Cookie;

            # Use streaming to avoid locking
            set beresp.do_stream = true;

            # Don't try to compress it for storage
            set beresp.do_gzip = false;

            # Disable Transfer-encoding chunked when serving HTML5 video
            # See : http://stackoverflow.com/questions/23643233/how-do-i-disable-transfer-encoding-chunked-encoding-in-varnish
            # See : https://www.varnish-cache.org/trac/ticket/1506
            if(beresp.http.Content-Type ~ "video") {
                set beresp.do_stream = true;
                set beresp.do_esi    = true;
            }
        }

        # Set 2min cache if unset for static files
        if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*")
        {
            set beresp.ttl         = 120s; # Important, you shouldn't rely on this, SET YOUR HEADERS in the backend
            set beresp.uncacheable = true;

            return (deliver);
        }
    }

    # Sometimes, a 301 or 302 redirect formed via Apache's mod_rewrite can mess with the HTTP port that is being passed along.
    # This often happens with simple rewrite rules in a scenario where Varnish runs on :80 and Apache on :8080 on the same box.
    # A redirect can then often redirect the end-user to a URL on :8080, where it should be :80.
    # This may need finetuning on your setup.
    #
    # To prevent accidental replace, we only filter the 301/302 redirects for now.
    if (beresp.status == 301 || beresp.status == 302) {
        set beresp.http.Location = regsub(beresp.http.Location, ":[0-9]+", "");
    }

    # Never cache 50x responses
    if (beresp.status >= 500) {
        return (abandon);
    }

    # Do not cache errors from the backend
    if (beresp.status >= 400)
    {
        set beresp.uncacheable = true;
        set beresp.ttl = 0s;

        return (deliver);
    }

    # Allow stale content, in case the backend goes down. Make Varnish keep all objects for 6 hours beyond their TTL
    set beresp.grace = 6h;

    # Set ban-lurker friendly custom headers
    set beresp.http.X-Varnish-Url  = bereq.url;
    set beresp.http.X-Varnish-Host = bereq.http.host;

    return (deliver);
}

# Called when the backend returns an error
sub vcl_backend_error
{
    if (beresp.status == 503 && bereq.retries < 5)
    {
        set beresp.http.X-Varnish-Retries = bereq.retries;
        return(retry);
    }

    return(deliver);
}

# Called before a cached object is delivered to the client.
sub vcl_deliver
{
    # Send the Cookie header again if a temporay header was stored
    if (req.http.X-Varnish-Cookie) {
        set resp.http.Set-Cookie = req.http.X-Varnish-Cookie;
    }

    # Add extra headers if debugging is enabled
    if (resp.http.x-varnish-debug && server.ip ~ localhost)
    {
        set resp.http.X-Served-By = server.hostname;

        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;
    }
    else
    {
        # Remove ban-lurker friendly custom headers when delivering to client
        unset resp.http.X-Url;
        unset resp.http.X-Host;

        # Remove some headers: PHP version
        unset resp.http.X-Powered-By;

        # Remove some headers: Apache version & OS
        unset resp.http.Server;
        unset resp.http.Via;
        unset resp.http.Link;
        unset resp.http.X-Varnish;
        unset resp.http.X-Varnish-Tag;
        unset resp.http.X-Varnish-Debug;
        unset resp.http.X-Varnish-Host;
        unset resp.http.X-Varnish-Url;
        unset resp.http.X-Varnish-Retries;
        unset resp.http.X-Varnish-Cookie;
        unset resp.http.Surrogate-Control;
    }

    return (deliver);
}

sub vcl_purge
{
    # Only handle actual PURGE HTTP methods, everything else is discarded
    if (req.method != "PURGE" && server.ip ~ localhost)
    {
        set req.http.X-Varnish-Purge = "Yes";
        return (restart);
    }
}

sub vcl_synth
{
    if (resp.status == 720)
    {
        # We use this special error status 720 to force redirects with 301 (permanent) redirects
        # To use this, call the following from anywhere in vcl_recv: return (synth(720, "http://host/new.html"));
        set resp.http.Location = resp.reason;
        set resp.status = 301;

        return (deliver);
    }
    elseif (resp.status == 721)
    {
        # And we use error status 721 to force redirects with a 302 (temporary) redirect
        # To use this, call the following from anywhere in vcl_recv: return (synth(720, "http://host/new.html"));

        set resp.http.Location = resp.reason;
         set resp.status = 302;

        return (deliver);
    }

    return (deliver);
}

# Called when VCL is discarded only after all requests have exited the VCL. Typically used to clean up VMODs.
sub vcl_fini
{
  return (ok);
}

vcl 4.0;
# Based on: https://github.com/mattiasgeniar/varnish-4.0-configuration-templates/blob/master/default.vcl

import std;
import directors;

probe health
{
    #.url = "/varnish-enabled";
    # We prefer to only do a HEAD /
    .request =
        "HEAD /varnish-enabled HTTP/1.1"
        "Host: localhost"
        "Connection: close";
        "User-Agent: Varnish Health Probe";
    .interval = 5s;   # check the health of each backend every 5 seconds
    .timeout = 1s;    # timing out after 1 second.
    # If 3 out of the last 5 polls succeeded the backend is considered healthy, otherwise it will be marked as sick
    .window = 5;
    .threshold = 3;
    .expected_response = 200;
}

backend default
{
    .host = "127.0.0.1";        # IP or Hostname of backend
    .port = "8080";             # Port Apache or whatever is listening
    .max_connections = 300;     # That's it
    .probe = health;
    .connect_timeout            = 600s;  # How long to wait before we receive a first byte from our backend?
    .first_byte_timeout         = 600s;  # How long to wait for a backend connection?
    .between_bytes_timeout      = 600s;  # How long to wait between bytes received from our backend?
}

backend passthrough
{
    .host = "127.0.0.1";        # IP or Hostname of backend
    .port = "8080";             # Port Apache or whatever is listening
}

acl localhost
{
    "localhost";
    "33.33.33.63";
    "::1";
}

sub vcl_init
{
    # Called when VCL is loaded, before any requests pass through it.
    # Typically used to initialize VMODs.

    new vdir = directors.round_robin();
    vdir.add_backend(default);
    # vdir.add_backend(server...);
    # vdir.add_backend(servern);
}

sub vcl_recv
{
    # Called at the beginning of a request, after the complete request has been received and parsed.
    # Its purpose is to decide whether or not to serve the request, how to do it, and, if applicable,
    # which backend to use and if needed to modify the request.

    # Send all traffic to the vdir director
    set req.backend_hint = vdir.backend();

    # Normalize the header, remove the port (in case you're testing this on various TCP ports)
    set req.http.Host = regsub(req.http.Host, ":[0-9]+", "");

    # Normalize the query arguments
    set req.url = std.querysort(req.url);

    # Allow purging
    if (req.method == "PURGE")
    {
        # purge is the ACL defined at the begining
        if (!client.ip ~ localhost) {
            return (synth(405, "This IP is not allowed to send PURGE requests."));
        }

        # If you got this stage (and didn't error out above), purge the cached result
        return (purge);
    }

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

    # Only deal with "normal" types
    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") {
        /* Non-RFC2616 or CONNECT which is weird. */
        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);
    }

    # Only cache GET or HEAD requests. This makes sure the POST requests are always passed.
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }

    # Specific Nooku Platform rules

        # Do not cache /administrator
        if(req.url ~ "^/administrator") {
            return (pass);
        }

    # Specific Nooku Server rules

        # Do not cache webgrind.nooku.dev
        if (req.http.host == "webgrind.nooku.dev") {
            return (pass);
        }

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

        # Do not cache /apc and /phpinfo
        if (req.url == "/apc" || req.url == "/phpinfo") {
            return (pass);
        }

    # Generic URL manipulation, useful for all templates that follow

        # Remove the Google Analytics added parameters, useless for our backend
        if (req.url ~ "(\?|&)(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=") {
            set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "");
            set req.url = regsuball(req.url, "\?(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "?");
            set req.url = regsub(req.url, "\?&", "?");
            set req.url = regsub(req.url, "\?$", "");
        }

        # Strip hash, server doesn't need it.
        if (req.url ~ "\#") {
            set req.url = regsub(req.url, "\#.*$", "");
        }

        # Strip a trailing ? if it exists
        if (req.url ~ "\?$") {
            set req.url = regsub(req.url, "\?$", "");
        }

    # Cookie manipulation

        # Remove the "has_js" cookie
        set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", "");

        # Remove any Google Analytics based cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "_gat=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmctr=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmcmd.=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmccn.=[^;]+(; )?", "");

        # Remove DoubleClick offensive cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "__gads=[^;]+(; )?", "");

        # Remove the Quant Capital cookies (added by some plugin, all __qca)
        set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", "");

        # Remove the AddThis cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "__atuv.=[^;]+(; )?", "");

        # Remove a ";" prefix in the cookie if present
        set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");

        # Remove the csrf_token cookie
        set req.http.Cookie = regsuball(req.http.Cookie, "csrf_token=[^;]+(; )?", "");

        # Are there cookies left with only spaces or that are empty?
        if (req.http.cookie ~ "^\s*$") {
            unset req.http.cookie;
        }

        # Large static files are delivered directly to the end-user without waiting for Varnish to fully read the file first.
        # Varnish 4 supports Streaming, so set do_stream in vcl_backend_response()
        if (req.url ~ "^[^?]*\.(7z|avi|bz2|flac|flv|gz|mka|mkv|mov|mp3|mp4|mpeg|mpg|ogg|ogm|opus|rar|tar|tgz|tbz|txz|wav|webm|xz|zip)(\?.*)?$") {
            unset req.http.Cookie;
            return (hash);
        }

        # Remove all cookies for static files
        # Before you blindly enable this read: https://ma.ttias.be/stop-caching-static-files/
        #if (req.method == "GET")
        #{
        #    # Cache files with these extensions and remove cookie
        #    if (req.url ~ "^[^?]*\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|otf|ogg|ogm|opus|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)(\?.*)?$") {
        #        unset req.http.Cookie;
        #        return (hash);
        #    }
        #}

    if (req.http.Cache-Control ~ "(?i)no-cache" && client.ip ~ localhost)
    {
        # http://varnish.projects.linpro.no/wiki/VCLExampleEnableForceRefresh
        # Ignore requests via proxy caches and badly behaved crawlers
        # like msnbot that send no-cache with every request.
        if (! (req.http.Via || req.http.User-Agent ~ "(?i)bot" || req.http.X-Purge))
        {
            # Doesn't seems to refresh the object in the cache
            set req.hash_always_miss = true;

            # Couple this with restart in vcl_purge and X-Purge header to avoid loops
            return(purge);
        }
    }

    # Send Surrogate-Capability headers to announce ESI support to backend
    set req.http.Surrogate-Capability = "varnish=ESI/1.0";

    # Requests that require authorization are not cacheable by default
    if (req.http.Authorization) {
        return (pass);
    }

    # We do not support SPDY or HTTP/2.0
    if (req.method == "PRI") {
        return (synth(405));
    }

    return (hash);
}

# Called upon entering pipe mode. In this mode, the request is passed on to the backend, and any further data from both
# the client and backend is passed on unaltered until either end closes the connection. Basically, Varnish will degrade
# into a simple TCP proxy, shuffling bytes back and forth. For a connection in pipe mode, no other VCL subroutine will
# ever get called after vcl_pipe.
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";
    # It is not set by default as it might break some broken web applications, like IIS with NTLM authentication.
    # set bereq.http.Connection = "Close";

    # Implementing websocket support (https://www.varnish-cache.org/docs/4.0/users-guide/vcl-example-websockets.html)
    if (req.http.upgrade) {
        set bereq.http.upgrade = req.http.upgrade;
    }

    return (pipe);
}

# Called upon entering pass mode. In this mode, the request is passed on to the backend, and the backend's response
# is passed on to the client, but is not entered into the cache. Subsequent requests submitted over the same client
# connection are handled normally.
sub vcl_pass
{
    return (fetch);
}

# Called after vcl_recv to create a hash value for the request. This is used as a key to look up the object in Varnish.
sub vcl_hash
{
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

    # hash cookies for requests that have them
    if (req.http.Cookie) {
        hash_data(req.http.Cookie);
    }
}

# Called when a cache lookup is successful.
sub vcl_hit
{
    # A pure unadultered hit, deliver it
    if (obj.ttl >= 0s) {
        return (deliver);
    }

    # https://www.varnish-cache.org/docs/trunk/users-guide/vcl-grace.html
    # When several clients are requesting the same page Varnish will send one request to the backend and place the
    # others on hold while fetching one copy from the backend. In some products this is called request coalescing and
    # Varnish does this automatically.

    # If you are serving thousands of hits per second the queue of waiting requests can get huge. There are two
    # potential problems - one is a thundering herd problem - suddenly releasing a thousand threads to serve content
    # might send the load sky high. Secondly - nobody likes to wait. To deal with this we can instruct Varnish to keep
    # the objects in cache beyond their TTL and to serve the waiting requests somewhat stale content.

    # if (!std.healthy(req.backend_hint) && (obj.ttl + obj.grace > 0s)) {
    #   return (deliver);
    # } else {
    #   return (fetch);
    # }

    # We have no fresh fish. Lets look at the stale ones.
    if (std.healthy(req.backend_hint))
    {
        # Backend is healthy. Limit age to 10s.
        if (obj.ttl + 10s > 0s)
        {
            #set req.http.grace = "normal(limited)";
            return (deliver);
        }
        else
        {
            # No candidate for grace. Fetch a fresh object.
            return(fetch);
        }
    }
    else
    {
        # backend is sick - use full grace
        if (obj.ttl + obj.grace > 0s)
        {
            #set req.http.grace = "full";
            return (deliver);
        }
        else
        {
            # no graced object.
            return (fetch);
        }
    }

    # fetch & deliver once we get the result
    return (fetch); # Dead code, keep as a safeguard
}

# Called after a cache lookup if the requested document was not found in the cache. Its purpose is to decide whether
# or not to attempt to retrieve the document from the backend, and which backend to use.
sub vcl_miss
{
    return (fetch);
}

 # Called after the response headers has been successfully retrieved from the backend.
sub vcl_backend_response
{
    # Enable ESI handling
    if (beresp.http.Surrogate-Control ~ "ESI/1.0" ) {
        set beresp.do_esi = true;
    }

    # Store the csrf_token cookie temporarily if the response is cacheabale
    if (beresp.http.Set-Cookie && !beresp.uncacheable)
    {
        set beresp.http.X-Varnish-Cookie = beresp.http.Set-Cookie;
        unset beresp.http.Set-Cookie;
    }

    # Cache handling all static files
    if (bereq.method == "GET")
    {
        # Before you blindly enable this, read: https://ma.ttias.be/stop-caching-static-files/
        #if (bereq.url ~ "^[^?]*\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|otf|ogg|ogm|opus|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)(\?.*)?$") {
        #    unset beresp.http.Set-Cookie;
        #}

        # Large static files are delivered directly to the end-user without waiting for Varnish to fully read the
        # file first. Check memory usage it'll grow in fetch_chunksize blocks (128k by default) if the backend
        # doesn't send a Content-Length header. Only enable it for large files
        if (bereq.url ~ "^[^?]*\.(7z|avi|bz2|flac|flv|gz|mka|mkv|mov|mp3|mp4|mpeg|mpg|ogg|ogm|opus|rar|tar|tgz|tbz|txz|wav|webm|xz|zip)(\?.*)?$") {
        {
            unset beresp.http.Set-Cookie;

            # Use streaming to avoid locking
            set beresp.do_stream = true;

            # Don't try to compress it for storage
            set beresp.do_gzip = false;

            # Disable Transfer-encoding chunked when serving HTML5 video
            # See : http://stackoverflow.com/questions/23643233/how-do-i-disable-transfer-encoding-chunked-encoding-in-varnish
            # See : https://www.varnish-cache.org/trac/ticket/1506
            if(beresp.http.Content-Type ~ "video") {
                set beresp.do_stream = true;
                set beresp.do_esi    = true;
            }
        }

        # Set 2min cache if unset for static files
        if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*")
        {
            set beresp.ttl         = 120s; # Important, you shouldn't rely on this, SET YOUR HEADERS in the backend
            set beresp.uncacheable = true;

            return (deliver);
        }
    }

    # Sometimes, a 301 or 302 redirect formed via Apache's mod_rewrite can mess with the HTTP port that is being passed along.
    # This often happens with simple rewrite rules in a scenario where Varnish runs on :80 and Apache on :8080 on the same box.
    # A redirect can then often redirect the end-user to a URL on :8080, where it should be :80.
    # This may need finetuning on your setup.
    #
    # To prevent accidental replace, we only filter the 301/302 redirects for now.
    if (beresp.status == 301 || beresp.status == 302) {
        set beresp.http.Location = regsub(beresp.http.Location, ":[0-9]+", "");
    }

    # Never cache 50x responses
    if (beresp.status >= 500) {
        return (abandon);
    }

    # Do not cache errors from the backend
    if (beresp.status >= 400)
    {
        set beresp.uncacheable = true;
        set beresp.ttl = 0s;

        return (deliver);
    }

    # Allow stale content, in case the backend goes down. Make Varnish keep all objects for 6 hours beyond their TTL
    set beresp.grace = 6h;

    # Set ban-lurker friendly custom headers
    set beresp.http.X-Varnish-Url  = bereq.url;
    set beresp.http.X-Varnish-Host = bereq.http.host;

    return (deliver);
}

# Called when the backend returns an error
sub vcl_backend_error
{
    if (beresp.status == 503 && bereq.retries < 5)
    {
        set beresp.http.X-Varnish-Retries = bereq.retries;
        return(retry);
    }

    return(deliver);
}

# Called before a cached object is delivered to the client.
sub vcl_deliver
{
    # Send the Cookie header again if a temporay header was stored
    if (req.http.X-Varnish-Cookie) {
        set resp.http.Set-Cookie = req.http.X-Varnish-Cookie;
    }

    # Add extra headers if debugging is enabled
    if (resp.http.x-varnish-debug && server.ip ~ localhost)
    {
        set resp.http.X-Served-By = server.hostname;

        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;
    }
    else
    {
        # Remove ban-lurker friendly custom headers when delivering to client
        unset resp.http.X-Url;
        unset resp.http.X-Host;

        # Remove some headers: PHP version
        unset resp.http.X-Powered-By;

        # Remove some headers: Apache version & OS
        unset resp.http.Server;
        unset resp.http.Via;
        unset resp.http.Link;
        unset resp.http.X-Varnish;
        unset resp.http.X-Varnish-Tag;
        unset resp.http.X-Varnish-Debug;
        unset resp.http.X-Varnish-Host;
        unset resp.http.X-Varnish-Url;
        unset resp.http.X-Varnish-Retries;
        unset resp.http.X-Varnish-Cookie;
        unset resp.http.Surrogate-Control;
    }

    return (deliver);
}

sub vcl_purge
{
    # Only handle actual PURGE HTTP methods, everything else is discarded
    if (req.method != "PURGE" && server.ip ~ localhost)
    {
        set req.http.X-Varnish-Purge = "Yes";
        return (restart);
    }
}

sub vcl_synth
{
    if (resp.status == 720)
    {
        # We use this special error status 720 to force redirects with 301 (permanent) redirects
        # To use this, call the following from anywhere in vcl_recv: return (synth(720, "http://host/new.html"));
        set resp.http.Location = resp.reason;
        set resp.status = 301;

        return (deliver);
    }
    elseif (resp.status == 721)
    {
        # And we use error status 721 to force redirects with a 302 (temporary) redirect
        # To use this, call the following from anywhere in vcl_recv: return (synth(720, "http://host/new.html"));

        set resp.http.Location = resp.reason;
         set resp.status = 302;

        return (deliver);
    }

    return (deliver);
}

# Called when VCL is discarded only after all requests have exited the VCL. Typically used to clean up VMODs.
sub vcl_fini
{
  return (ok);
}


#674: Pantheon adapted varnish VCL file

#674: Pantheon adapted varnish VCL file

vcl 4.0;
# For debugging, use the following and inspect the output of `varnishlog`:
#
# import std;
# std.log("this is my debug log message");
import std;

backend styxgo {
  .host = "127.0.0.1";
  .port = "8888";
  .connect_timeout = 600s;
  .first_byte_timeout = 600s;
  .between_bytes_timeout = 600s;
}

sub vcl_recv {
  set req.backend_hint = styxgo;

  # Always have a `has_js=1` cookie. This is set by Drupal to indicate that the
  # client browser supports JavaScript.
  # TODO (Mark): It isn't clear *why* we want to force has_js=1, update this
  #              comment if you know, please.
  #
  # NOTE(jesse): WAT
  #   https://www.drupal.org/node/229825  seems to be there reason why here?
  if (req.http.Cookie) {
    if (req.http.Cookie !~ "has_js=1") {
      set req.http.Cookie = "has_js=1; " + req.http.Cookie;
    }
  } else {
    set req.http.Cookie = "has_js=1";
  }

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

  # Do not cache these paths, note that sometimes query strings are appended.
  if (req.url ~ "^/install\.php" ||
      req.url ~ "^/update\.php" ||
      req.url ~ "^/cron\.php") {
        return (pass);
  }

  # Pipe backup_migrate directly
  if (req.url ~ "^/admin/content/backup_migrate/export" || req.url ~ "^/system/files") {
    return (pipe);
  }

  # Do not cache potentially large files.
  if (req.url ~ "(?i)\.(mp3|flv|mov|mp4|mpg|mpeg|avi|mkv|ogg|oggv|wmv|rm|m4v|3gp|dmg|pdf)($|\?)") {
    return (pipe);
  }

  # Specify the ESI capability (http://www.w3.org/TR/edge-arch)
  # Note: This clobbers upstream capabilities, should nicely append styx's capabilities.
  set req.http.Surrogate-Capability = {"styx="ESI/1.0""};

  # Normalize the Accept-Encoding header. See http://varnish-cache.org/wiki/FAQ/Compression
  if (req.http.Accept-Encoding) {
    if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$" || req.url ~ "^/robots\.txt$") {
      # No point in compressing these
      unset req.http.Accept-Encoding;
    }
    elsif (req.http.Accept-Encoding ~ "gzip") {
      set req.http.Accept-Encoding = "gzip";
    }
    else {
      # unkown algorithm
      unset req.http.Accept-Encoding;
    }
  }

  # Strip all cookies on requests for asset files. This ensures cache-busting
  # session cookies do not cause cache misses for these.
  #
  # Query strings may have a random series of letters/numbers (Drupal doing its
  # cache-busting), or it may have "itok=xyz" which is for image derivatives, or
  # in the case of Wordpress, it may have "ver=x.y.z" for css / js files. We can
  # safely remove cookies on all of these, as static files don't vary according
  # to a user's session cookie.
  #
  # In our nginx configuration, we explicitly set assets returned in dev and
  # multidev environments to expire in the past, which essentially overrides
  # any Varnish configuration that might attempt to cache those assets. This
  # is so that developers always have fresh content while editing files.
  #
  # This list of file extensions is also in vcl_backend_response(), keep them in sync.
  if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|bmp|tif|tiff|webp|swf|css|js)($|\?)") {
    unset req.http.Cookie;
  }

  # WARNING: Note that any code below the following will not be processed for
  # POST and other non-GET and non-HEAD requests!

  # Any unknown (Non-RFC2616 or CONNECT) request methods will be piped to the backend,
  # as we can't make any caching decisions about those. We need to allow PURGE to
  # pass as cookie munging is important for purging the correct object.
  if (req.method != "GET" &&
    req.method != "HEAD" &&
    req.method != "PUT" &&
    req.method != "POST" &&
    req.method != "TRACE" &&
    req.method != "OPTIONS" &&
    req.method != "DELETE" &&
    req.method != "PURGE") {
      return (pipe);
  }

  # We only cache GET and HEAD requests, so allow requests for other HTTP verbs
  # to pass through without stripping cookies, etc.
  if (req.method != "GET" &&
    req.method != "HEAD" &&
    req.method != "PURGE") {
    return (pass);
  }

  # Strip away Google analytics cruft from GET queries.
  # https://pantheon-systems.desk.com/agent/case/976
  # Based on original GA cookie cleaner
  # A previous version of the match was for (__[a-z|_]) and utm_[a-z|_].
  # Is the pipe (|) intended to match a literal pipe, or is it an error
  # signifying 'or' as in a-z or _, and doesn't belong? Leaving it for now.
  if (req.url ~ "(\?|\&)(__[a-z|_A-Z-]+|utm_[a-z|_A-Z-]+)") {
    # Side-save magic: capture the portion of the url that starts the utm block.
    # We will reverse this below in vcl_deliver() if there's a redirect, so
    # customers don't end up with PANTHEON_STRIPPED all up in their GA reports
    # just because they put out a link to a redirect.
    #
    # This process isn't perfect. The capture will take everything from the beginning
    # of the first occurrance of utm_, and restore it the same way. If a redirect
    # is triggered, and the redirect appends query parameters after the first utm_,
    # they will be lost. This could be fixed robustly by not using regular
    # expressions here in this fashion, and instead using a Varnish module (vmod)
    # to handle it. Or better targeting of the utm_ var names.
    set req.http.X-Pre-Strip = regsub(req.url, ".*?.*?(utm_.*)", "\1");

    # Notice that even though the Pre-Strip is captured in a basic fashion for redirects,
    # in general stripping is quite precise. Here the __xyz and utm_ params are
    # correctly carved out leaving any preceding and following params intact.
    set req.url = regsuball(req.url, "(\?|\&)(__[a-z|_A-Z-]+|utm_[a-z|_A-Z-]+)=([^\&]*)", "\1\2=PANTHEON_STRIPPED");
  }

  # Cookie munging!
  if (req.http.Cookie) {
    # Prefix the existing cookie header with ";" to make all cookies
    # have the form ";$name=$value" for better regexing.
    set req.http.Cookie = ";" + req.http.Cookie;

    # Remove any spaces between semicolons and the beginnings of cookie names.
    set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");

    # Nuke the annoyingly patterned wordpress_test_cookie
    set req.http.Cookie = regsuball(req.http.Cookie, ";wordpress_test[^;]*", "");

    # We bypass the cache if there is a NO_CACHE or login-related cookie set.
    # Also check for SimpleSAML or Facebook cookies to support external auth.

    if (req.http.Cookie ~ ";NO_CACHE=" ||
      req.http.Cookie ~ ";S+ESS[a-z0-9]+=" ||
      req.http.Cookie ~ ";fbs[a-z0-9_]+=" ||
      req.http.Cookie ~ ";SimpleSAML[A-Za-z]+=" ||
      req.http.Cookie ~ ";SimpleSAML[A-Za-z]+=" ||
      req.http.Cookie ~ ";PHPSESSID=" ||
      req.http.Cookie ~ ";wordpress[A-Za-z0-9_]*=" ||
      req.http.Cookie ~ ";wp-[A-Za-z0-9_]+=" ||
      req.http.Cookie ~ ";comment_author_[a-z0-9_]+=" ||
      req.http.Cookie ~ ";duo_wordpress_auth_cookie=" ||
      req.http.Cookie ~ ";duo_secure_wordpress_auth_cookie=" ||
      req.http.Cookie ~ ";bp_completed_create_steps=" ||
      req.http.Cookie ~ ";bp_new_group_id=" ||
      req.http.Cookie ~ ";wp-resetpass-[A-Za-z0-9_]+=" ||
      req.http.Cookie ~ ";(wp_)?woocommerce[A-Za-z0-9_-]+=") {

      # This flag will be picked up further down vcl_recv() and will trigger a pass.
      set req.http.X-Bypass-Cache = "1";
    }

    # Add back a space between the semicolon and cookie name for each cookie we
    # intend to pass to the back-end. Starting with login-related cookies.
    set req.http.Cookie = regsuball(req.http.Cookie, ";(NO_CACHE=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(S+ESS[a-z0-9]+=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(fbs[a-z0-9_]+=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(SimpleSAML[A-Za-z]+=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(SimpleSAML[A-Za-z]+=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(PHPSESSID=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(wordpress[A-Za-z0-9_]*=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(wp-[A-Za-z0-9_]+=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(comment_author_[a-z0-9_]+=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(duo_wordpress_auth_cookie=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(duo_secure_wordpress_auth_cookie=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(bp_completed_create_steps=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(bp_new_group_id=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(wp-resetpass-[A-Za-z0-9_]+=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";((wp_)?woocommerce[A-Za-z0-9_-]+=)", "; \1");

    # Same thing for cache-varying cookies: add back a space between the semicolon
    # and cookie name. These are different to the login-related cookies above, as we
    # allow caching for these; we will keep a separate copy of the content per cookie value.
    set req.http.Cookie = regsuball(req.http.Cookie, ";(STYXKEY[a-zA-Z0-9_-]+=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(has_js=)", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(Drupal[a-zA-Z0-9_\.-]+=)", "; \1");

    # This is a BuddyPress pattern used in filtering/sorting content.
    set req.http.Cookie = regsuball(req.http.Cookie, ";(bp-[a-z]+-(scope|filter))", "; \1");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(bp-message-[A-Za-z1-9_-]+)", "; \1");

    # WARNING: When adding more cache-varying cookies here, update the `purge_varnish`
    # task in Yggdrasil, which also maintains a list of these cookies.

    # Strip all cookies lacking the telltale space between the semicolon and cookie name.
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");

    # Replace any leading or trailing spaces and semicolons. This leaves
    # a completely empty cookie header if there are no cookies left.
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

    # If the cookie header is empty, drop it entirely.
    if (req.http.Cookie == "") {
      unset req.http.Cookie;
    }
  }

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

  # We perform a lookup even if there are cookies, as any cookies present on a
  # request that has made it this far in vcl_recv(), will be used as a Cache-Varying
  # cookie in vcl_hash().
  return (hash);
}

sub vcl_pipe {
  # Called upon entering pipe mode.
  # In this mode, the request is passed on to the backend, and any further data from both the client
  # and backend is passed on unaltered until either end closes the connection. Basically, Varnish will
  # degrade into a simple TCP proxy, shuffling bytes back and forth. For a connection in pipe mode,
  # no other VCL subroutine will ever get called after 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.
  set bereq.http.connection = "close";

  # Implementing websocket support (https://www.varnish-cache.org/docs/4.0/users-guide/vcl-example-websockets.html)
  if (req.http.upgrade) {
    set bereq.http.upgrade = req.http.upgrade;
  }
}

sub vcl_backend_response {
  # Don't allow static files to set cookies. This list of file extensions is also
  # in vcl_recv(), keep them in sync.
  if (bereq.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|bmp|tif|tiff|webp|swf|css|js)($|\?)") {
    unset beresp.http.set-cookie;
  }

  # Only cache 404s in the filesystem for 30s.
  if (bereq.url ~ "^/sites/default/files/" && beresp.status == 404) {
    set beresp.ttl = 30s;
    set beresp.http.Cache-Control = "public, max-age=30";
  }

  # Caching for permanent redirects (301s).
  if (beresp.status == 301) {
    if (beresp.http.Location == bereq.http.X-Proto + bereq.http.Host + bereq.url) {
      # Do not cache requests that 301 to themselves for very long.
      set beresp.http.Cache-Control = "public, max-age=3";
      set beresp.ttl = 3s;
      set beresp.http.X-Pantheon-Debug = "Circular redirect detected.";
    }
    else if (beresp.http.Location ~ "/\?q=") {
      # Do not cache 301s that redirect to ?q=
      # Misfiring clean urls
      # https://pantheon-systems.desk.com/agent/case/15792
      set beresp.http.Cache-Control = "public, max-age=3";
      set beresp.ttl = 3s;
      set beresp.http.X-Pantheon-Debug = "Wow. Such redirect.";
    }
    else if (beresp.http.x-pantheon-endpoint ~ ",") {
      # Comma in your endpoint header? Go jump in a lake. This is a bug.
      set beresp.http.Cache-Control = "no-cache, must-revalidate, post-check=0, pre-check=0";
      set beresp.ttl = 0s;
    }
    else if (!beresp.http.Cache-Control) {
      # Otherwise if no headers, a day is good.
      set beresp.ttl = 1d;
      set beresp.http.Cache-Control = "public, max-age=86400";
    }
  }

  # Enable ESI processing if requested
  if (beresp.http.Surrogate-Control ~ "ESI/1.0" && !bereq.http.X-Bypass-Pantheon-Esi) {
    unset beresp.http.Surrogate-Control;
    set beresp.do_esi = true;
    set beresp.http.X-Styx-Attempted-ESI = "1";
  }

  # Don't cache a redirect to install.php
  if (beresp.http.Location ~ "/install.php$") {
    # set beresp.ttl = 120s;
    set beresp.uncacheable = true;
    return (deliver);
  }

  # Strip content encoding for 301 and 302 responses.
  # The content is useless to browsers, and an incorrect
  # encoding causes Varnish 3 to return a 503 because
  # Varnish 3 is encoding-aware.
  if (beresp.status == 301 || beresp.status == 302) {
    unset beresp.http.content-encoding;
  }

  # beresp.grace is the length of time Varnish will keep cache objects beyond their TTL.
  # So if we have a header `Cache-Control: public, max-age=900`, we'll keep that for 900
  # seconds + the grace period here. This doesn't actually cause the stale objects to be
  # served; for that, see `bereq.grace` in `vcl_recv()`. This value must be at minimum the
  # value of bereq.grace, otherwise we would be essentially telling Varnish to serve stale
  # content for a longer period of time than we're actually storing it.
  set beresp.grace = 120m;

  # Smart bans. Varnish has a ban lurker thread, which will test old objects against
  # bans periodically, without a client. For it to work, your bans can not refer to anything
  # starting with `req`, as the ban lurker doesn’t have any request data structure.
  #
  # @see: https://www.varnish-software.com/static/book/Cache_invalidation.html
  set beresp.http.x-url = bereq.url;
  set beresp.http.x-host = bereq.http.host;

  # Varnish ignores 'no-cache' by itself and only looks at 'max-age' in the
  # Cache-Control headers.
  #
  # So, if the response's Cache-Control has only half the required pieces,
  # add the rest in here so we don't cache the page. This is mostly to
  # support customers who are executing PHP outside of Drupal/Wordpress.
  if (beresp.http.Cache-Control == "no-cache") {
    set beresp.ttl = 0s;
    set beresp.http.Cache-Control = "no-cache, must-revalidate, post-check=0, pre-check=0";
    # set beresp.ttl = 120s;
    set beresp.uncacheable = true;
    return (deliver);
  }
}

sub vcl_hash {
  # Called after vcl_recv to create a hash value for the request. This is used as a key
  # to look up the object in Varnish.

  if (req.http.X-Forwarded-Proto) {
    hash_data(req.http.X-Forwarded-Proto);
  }
  if (req.http.X-SSL) {
    hash_data(req.http.X-SSL);
  }
  if (req.http.Cookie) {
    hash_data(req.http.Cookie);
  }
}
sub vcl_miss {
  # go fetch!
  return (fetch);
}

sub vcl_hit {
  if (obj.ttl >= 0s) {
    # A pure unadultered hit, deliver it
    return (deliver);
  }

  # https://www.varnish-cache.org/docs/trunk/users-guide/vcl-grace.html

# if (!std.healthy(req.backend_hint) && (obj.ttl + obj.grace > 0s)) {
#   return (deliver);
# } else {
#   return (fetch);
# }

  # We have no fresh fish. Lets look at the stale ones.
  if (std.healthy(req.backend_hint)) {
    # Backend is healthy. Limit age to 10s.
    if (obj.ttl + 10s > 0s) {
      #set req.http.grace = "normal(limited)";
      return (deliver);
    } else {
      # No candidate for grace. Fetch a fresh object.
      return(fetch);
    }
  } else {
    # backend is sick - use full grace
      if (obj.ttl + obj.grace > 0s) {
      #set req.http.grace = "full";
      return (deliver);
    } else {
      # no graced object.
      return (fetch);
    }
  }

  # fetch & deliver once we get the result
  return (fetch); # Dead code, keep as a safeguard
}

sub vcl_deliver {

  # the following is commented out for security reasons. Do not expose the public IP's of styx boxes because
  # it can give attackers the ability to attack styx nodes directly and bypass any masking that someone may be doing
  # by pointing their DNS through a filtering service like Cloudflare.
  # set resp.http.X-Pantheon-Edge-Server = server.ip;
  if (resp.http.Content-Type ~ "(text/html)") {
    set resp.http.Vary = resp.http.Vary + ", Cookie";
    # We are busting browser caching on any html pages for Safari since it
    # does not RESPECT the Vary on Cookie header.
    if (req.http.user-agent ~ "Safari" && !req.http.user-agent ~ "Chrom") {
      set resp.http.Cache-Control = "no-cache";
    }
  }

  # Reverse the side-save magic for PANTHEON_STRIPPED, see `vcl_recv()` function.
  if ((resp.status == 301 || resp.status == 302) && req.http.X-Pre-Strip && req.http.X-Pre-Strip != "") {
    set resp.http.X-Pre-Strip-Debug = req.http.X-Pre-Strip;

    # If the backend responds with a redirect, but the Location header doesn't contain
    # any utm_ query string parameters, but we know we replaced them with PANTHEON_STRIPPED,
    # then we do the customer a favor and put them back again. We shouldn't really do this
    # but core Drupal does lose params when `drupal_goto('abcdef')` is called.
    if (resp.http.Location !~ "(\?|\&)utm_.*") {

      if (resp.http.Location !~ ".*\?") {
        # If there are no query parameters on the Location response, append a `?`. We
        # will be the only parameter in this case.
        set req.http.X-Pre-Strip = "?" + req.http.X-Pre-Strip;
      }
      else {
        set req.http.X-Pre-Strip = "&" + req.http.X-Pre-Strip;
      }
      set resp.http.Location = resp.http.Location + req.http.X-Pre-Strip;
    }
    else {
      # Strip out utm_* from the Location header, then add the values we stripped back on.
      # This results in the utm_ parameters always being added to the end of the Location
      # header.
      set resp.http.Location = regsub(resp.http.Location, "(.*\?.*?)utm_.*", "\1") + req.http.X-Pre-Strip;
    }
  }
}

vcl 4.0;

backend default {
  .host =
  .port = "8888";
}

sub vcl_recv {

  # Return (pass) instructs Varnish not to cache the request
  # when the condition is met.

  ## GOOGLE ANALYTICS

  # Strip out Google Analytics variables
  if (req.url ~ "(\?|&)(__+|gclid|cx|ie|cof|siteurl|zanpid|origin|utm_[a-z]+|mr:[A-z]+)=") {
    set req.url = regsuball(req.url, "(__+gclid|cx|ie|cof|siteurl|zanpid|origin|utm_[a-z]+|mr:[A-z]+)=[%\._A-z0-9-]+&?", "");
  }

  ## ADMIN PAGES ##

  # Here we filter out all URLs containing Drupal administrative sections
  if (req.url ~ "^/status\.php$" ||
    req.url ~ "^/update\.php$" ||
    req.url ~ "^/admin$" ||
    req.url ~ "^/admin/.*$" ||
    req.url ~ "^/user$" ||
    req.url ~ "^/user/.*$" ||
    req.url ~ "^/flag/.*$" ||
    req.url ~ "^.*/ajax/.*$" ||
    req.url ~ "^.*/ahah/.*$" ||
    req.url ~ ".*fbconnect.*" ||
    req.url ~ ".*facebook.*" ||
    req.url ~ ".*fblink.*") {
      return (pass);
  }

  ## BACKUP AND MIGRATE MODULE ##

  # Backup and Migrate is a very popular Drupal module that needs to be excluded
  # It won't work with Varnish
  if (req.url ~ "^/admin/content/backup_migrate/export") {
    return (pipe);
  }

  ## COOKIES ##

  # Remove cookies for stylesheets, scripts, and images used throughout the site.
  # Removing cookies will allow Varnish to cache those files.
  if (req.url ~ "(?i)\.(css|js|jpg|jpeg|gif|png|ico)(\?.*)?$") {
    unset req.http.Cookie;
  }

  # Remove all cookies that are not necessary for Drupal to work properly.
  # Since it would be cumbersome to REMOVE certain cookies, we specify
  # which ones are of interest to us, and remove all others. In this particular
  # case we leave SESS, SSESS and NO_CACHE cookies used by Drupal's administrative
  # interface. Cookies in cookie header are delimited with ";", so when there are
  # many cookies, the header looks like "Cookie1=value1; Cookie2=value2; Cookie3..."
  # and so on. That allows us to work with ";" to split cookies into individual
  # ones.
  #
  # The method for filtering unnecessary cookies has been adopted from:
  # https://fourkitchens.atlassian.net/wiki/display/TECH/Configure+Varnish+3+for+Drupal+7
  if (req.http.Cookie) {

    # 1. We add ; to the beginning of cookie header
    set req.http.Cookie = ";" + req.http.Cookie;

    # 2. We remove spaces following each occurence of ";". After this operation
    # all cookies are delimited with no spaces.
    set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");

    # 3. We replace ";" INTO "; " (adding the space we have previously removed) in cookies
    # named SESS..., SSESS... and NO_CACHE. After this operation those cookies will be
    # easy to differentiate from the others, because those will be the only one with space
    # after ";"
    set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE)=", "; \1=");

    # 4. We remove all cookies with no space after ";", so basically we remove all cookies other
    # than those above.
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");

    # 5. We strip leading and trailing whitespace and semicolons.
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

    # 6. We whitelist some other useful cookies
    set req.http.Cookie = regsuball(req.http.Cookie, ";(fbs[a-z0-9_]+)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(SimpleSAML[A-Za-z]+)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(PHPSESSID)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(wordpress[A-Za-z0-9_]*)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(wp-[A-Za-z0-9_]+)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(comment_author_[a-z0-9_]+)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(duo_wordpress_auth_cookie)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(duo_secure_wordpress_auth_cookie)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(bp_completed_create_steps)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(bp_new_group_id)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(wp-resetpass-[A-Za-z0-9_]+)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";((wp_)?woocommerce[A-Za-z0-9_-]+)=", "; \1=");

    # If there are no cookies after our striping procedure, we remove the header altogether,
    # thus allowing Varnish to cache this page
    if (req.http.Cookie == "") {
      unset req.http.Cookie;
    }
    # if any of our cookies of interest are still there, we disable caching and pass the request
    # straight to Apache and Drupal
    else {
      return (pass);
    }
  }
}

sub vcl_backend_response {
  # Remove cookies for stylesheets, scripts and images used throughout the site.
  # Removing cookies will allow Varnish to cache those files. It is uncommon for
  # static files to contain cookies, but it is possible for files generated
  # dynamically by Drupal. Those cookies are unnecessary, but could prevent files
  # from being cached.
  if (bereq.url ~ "(?i)\.(css|js|jpg|jpeg|gif|png|ico)(\?.*)?$") {
    unset beresp.http.set-cookie;
  }
}


Avoid Stale-While-Revalidate problems

Avoid Stale-While-Revalidate problems

sub vcl_recv {
  # Disable Stale-While-Revalidate if a shield request to avoid double SWR
  if (req.http.Fastly-FF) {
    set req.max_stale_while_revalidate = 0s;
  }

  #FASTLY recv

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

  return(lookup);
}

sub vcl_fetch {
  #FASTLY fetch

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

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

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

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

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

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

  return(deliver);
}

sub vcl_hit {
  #FASTLY hit

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

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

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

sub vcl_error {
  #FASTLY error
}

sub vcl_pass {
  #FASTLY pass
}

sub vcl_log {
  #FASTLY log
}

sub vcl_recv {
  #FASTLY recv

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

  return(lookup);
}

sub vcl_fetch {
  #FASTLY fetch

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

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

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

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

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

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

  return(deliver);
}

sub vcl_hit {
  #FASTLY hit

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

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

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

sub vcl_error {
  #FASTLY error
}

sub vcl_pass {
  #FASTLY pass
}

sub vcl_log {
  #FASTLY log
}


Update openstreetmap3.vcl

Update openstreetmap3.vcl

# VCL script for Varnish 3

backend default {
  .host = "127.0.0.1";
  .port = "80";
}

backend a_tile {
  .host = "a.tile.openstreetmap.org";
  .port = "80";
}

backend b_tile {
  .host = "b.tile.openstreetmap.org";
  .port = "80";
}

backend c_tile {
  .host = "c.tile.openstreetmap.org";
  .port = "80";
}

sub vcl_recv {

  if (req.http.host ~ "^a.tile") {
    set req.backend = a_tile;
  } else if (req.http.host ~ "^b.tile") {
    set req.backend = b_tile;
  } else if (req.http.host ~ "^c.tile") {
    set req.backend = c_tile;
  } else if (req.http.host ~ "^a.map") {
    set req.backend = a_tile;
  } else if (req.http.host ~ "^b.map") {
    set req.backend = b_tile;
  } else if (req.http.host ~ "^c.map") {
    set req.backend = c_tile;
  }

  remove req.http.cookie;

  // Cache everything
  if (req.request == "GET") {
    return (lookup);
  }

}

sub vcl_fetch {

  // Cache tiles for 3 weeks
  set beresp.ttl = 3w;

  // Remove all cookies
  remove beresp.http.set-cookie;
  remove beresp.http.cookie;

}

sub vcl_deliver {

  if (obj.hits > 0) {
    set resp.http.X-Cache_v = "HIT";
  } else {
    set resp.http.X-Cache_v = "MISS";
  }
}

sub vcl_hash {

  // Cache using only url as a hash.
  // This means if a.tile/1/1/1/tile.png is accessed, b.tile/1/1/1/tile.png will also be fetch from cache
  hash_data(req.url);
  return (hash);
}

# VCL script for Varnish 3

backend default {
  .host = "127.0.0.1";
  .port = "80";
}

backend a_tile {
  .host = "a.tile.openstreetmap.org";
  .port = "80";
}

backend b_tile {
  .host = "b.tile.openstreetmap.org";
  .port = "80";
}

backend c_tile {
  .host = "c.tile.openstreetmap.org";
  .port = "80";
}

sub vcl_recv {

  if (req.http.host ~ "^a.tile") {
    set req.backend = a_tile;
  } else if (req.http.host ~ "^b.tile") {
    set req.backend = b_tile;
  } else if (req.http.host ~ "^c.tile") {
    set req.backend = c_tile;
  } else if (req.http.host ~ "^a.map") {
    set req.backend = a_tile;
  } else if (req.http.host ~ "^b.map") {
    set req.backend = b_tile;
  } else if (req.http.host ~ "^c.map") {
    set req.backend = c_tile;
  }

  remove req.http.cookie;

  // Cache everything
  if (req.request == "GET") {
    return (lookup);
  }

}

sub vcl_fetch {

  // Cache tiles for 3 weeks
  set beresp.ttl = 3w;

  // Remove all cookies
  remove beresp.http.set-cookie;
  remove beresp.http.cookie;

}

sub vcl_deliver {

  if (obj.hits > 0) {
    set resp.http.X-Cache_v = "HIT";
  } else {
    set resp.http.X-Cache_v = "MISS";
  }
}

sub vcl_hash {

  // Cache using only url as a hash.
  // This means if a.tile/1/1/1/tile.png is access, b.tile/1/1/1/tile.png will also be fetch from cache
  hash_data(req.url);
  return (hash);
}


Handle "Vary: *" headers by sending the object to pass, there really
isn't anything else we can do sensibly.

Fixes: #787

git-svn-id: 2c9807fa3ff65b17195bd55dc8a6c4261e10127b@5399 d4fa192b-c00b-0410-8231-f00ffab90ce4

/*-
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2009 Linpro 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.
 *
 * $Id$
 *
 * The 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.
 */

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.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_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 (pass);
}

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

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

sub vcl_miss {
    return (fetch);
}

sub vcl_fetch {
    if (!beresp.cacheable) {
        return (pass);
    }
    if (beresp.http.Set-Cookie) {
        return (pass);
    }
    if (beresp.http.Vary == "*") {
        return (pass);
    }
    return (deliver);
}

sub vcl_deliver {
    return (deliver);
}

sub vcl_error {
    set obj.http.Content-Type = "text/html; charset=utf-8";
    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 + {"

Varnish cache server

"};
    return (deliver);
}

/*-
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2009 Linpro 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.
 *
 * $Id$
 *
 * The 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.
 */

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.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_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 (pass);
}

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

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

sub vcl_miss {
    return (fetch);
}

sub vcl_fetch {
    if (!beresp.cacheable) {
        return (pass);
    }
    if (beresp.http.Set-Cookie) {
        return (pass);
    }
    return (deliver);
}

sub vcl_deliver {
    return (deliver);
}

sub vcl_error {
    set obj.http.Content-Type = "text/html; charset=utf-8";
    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 + {"

Varnish cache server

"};
    return (deliver);
}


2.2.1

import dclass;
#import urlcode;

backend default {
	.host = "localhost";
	.port = "80";
}

sub vcl_init {
	# load openddr
	dclass.init_dclass("/some/path/dClass/dtrees/openddr.dtree");
	#dclass.init_dclass_p("/some/path/OpenDDR/1.0.0.0/resources",0);

	# load browser dtree
	dclass.init_dclass_p("/some/path/dClass/dtrees/browser.dtree",1);
}

sub vcl_recv {
	# ua override
	if(req.url ~ "[^\w]ua="){
		set req.http.user-agent = regsub(req.url,"^.*[^\w]ua=([^&]*).*","\1");
		#set req.http.user-agent = urlcode.decode(regsub(req.url,"^.*[^\w]ua=([^&]*).*","\1"));
	}

	# do the lookup
	set req.http.dclass_openddr = dclass.classify(req.http.user-agent);
	#set req.http.dclass_openddr = dclass.classify_p(req.http.user-agent,0);
	set req.http.dclass_browser = dclass.classify_p(req.http.user-agent,1);

	# set dclass_type
	set req.http.dclass_type = "unknown";
	if(dclass.get_field("is_tablet") == "true"){
		set req.http.dclass_type = "tablet";
	} else if(dclass.get_field("is_wireless_device") == "true" && dclass.get_field("inputDevices") == "touchscreen"){
		set req.http.dclass_type = "smartphone";
	} else if(dclass.get_field("is_wireless_device") == "true"){
		set req.http.dclass_type = "phone";
	} else if(dclass.get_field("is_crawler") == "true"){
		set req.http.dclass_type = "crawler";
	} else if(dclass.get_field("is_desktop") == "true"){
		set req.http.dclass_type = "desktop";
	}

	# /uadiag commands
	if(req.url ~ "^/uadiag.js"){
		error 849 "uadiag.js";
	} else if(req.url ~ "^/uadiag"){
		error 848 "uadiag";
	}
}

# add dclass_type to the hash
sub vcl_hash {
	hash_data(req.url);

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

    #use this if you are serving different device content on the same URL
	hash_data(req.http.dclass_type);

	return (hash);
}

sub vcl_error {
	# plain text ua diag
	if(obj.status == 848){
		set obj.status = 200;
		set obj.http.Content-Type = "text/html; charset=utf-8";
		synthetic
{"<title>UADIAG</title>

UADIAG

User-Agent:       "} + req.http.User-Agent + {"

dClass version:   "} + dclass.get_version() + {"

dClass type:      "} + req.http.dclass_type + {"

OpenDDR id:       "} + req.http.dclass_openddr + {"
OpenDDR device:   "} + dclass.get_field("vendor") + {" "} + dclass.get_field("model") + {"
OpenDDR display:  "} + dclass.get_ifield("displayWidth") + {"x"} + dclass.get_ifield("displayHeight") + {"
OpenDDR input:    "} + dclass.get_field("inputDevices") + {"
OpenDDR js:       "} + dclass.get_field("ajax_support_javascript") + {"
OpenDDR wireless: "} + dclass.get_field("is_wireless_device") + {"
OpenDDR tablet:   "} + dclass.get_field("is_tablet") + {"
OpenDDR crawler:  "} + dclass.get_field("is_crawler") + {"
OpenDDR desktop:  "} + dclass.get_field("is_desktop") + {"

browser id:       "} + req.http.dclass_browser + {"
browser name:     "} + dclass.get_field_p("browser",1) + {"
browser ver:      "} + dclass.get_ifield_p("version",1) + {"
browser os:       "} + dclass.get_field_p("os",1) + {"

"};
		return(deliver);
	}
	# json ua diag with optional jsonp callback parameter
	else if(obj.status == 849){
		set obj.status = 200;
		set req.http.dclass_cb = "";
		set req.http.dclass_cbe = "";
		if(req.url ~ "[^\w]callback=\w+"){
			set obj.http.Content-Type = "application/javascript; charset=utf-8";
			set req.http.dclass_cb = regsub(req.url,"^.*[^\w]callback=(\w+).*","\1(");
			set req.http.dclass_cbe = ");";
		} else {
			set obj.http.Content-Type = "application/json; charset=utf-8";
		}
		synthetic
req.http.dclass_cb + {"{
"user_agent":""} + req.http.User-Agent + {"",
"dclass_version":""} + dclass.get_version() + {"",
"openddr_id":""} + req.http.dclass_openddr + {"",
"openddr_type":""} + req.http.dclass_type + {"",
"openddr_device":""} + dclass.get_field("vendor") + {" "} + dclass.get_field("model") + {"",
"openddr_display":""} + dclass.get_ifield("displayWidth") + {"x"} + dclass.get_ifield("displayHeight") + {"",
"openddr_input":""} + dclass.get_field("inputDevices") + {"",
"openddr_js":""} + dclass.get_field("ajax_support_javascript") + {"",
"openddr_wireless":""} + dclass.get_field("is_wireless_device") + {"",
"openddr_tablet":""} + dclass.get_field("is_tablet") + {"",
"openddr_crawler":""} + dclass.get_field("is_crawler") + {"",
"openddr_desktop":""} + dclass.get_field("is_desktop") + {"",
"browser_id":""} + req.http.dclass_browser + {"",
"browser_name":""} + dclass.get_field_p("browser",1) + {"",
"browser_ver":""} + dclass.get_ifield_p("version",1) + {"",
"browser_os":""} + dclass.get_field_p("os",1) + {""
}"} + req.http.dclass_cbe;
		return(deliver);
	}
}

import dclass;
#import urlcode;

backend default {
	.host = "localhost";
	.port = "80";
}

sub vcl_init {
	# load openddr
	dclass.init_dclass("/some/path/dClass/dtrees/openddr.dtree");
	#dclass.init_dclass_p("/some/path/OpenDDR/1.0.0.0/resources",0);

	# load browser dtree
	dclass.init_dclass_p("/some/path/dClass/dtrees/browser.dtree",1);
}

sub vcl_recv {
	# ua override
	if(req.url ~ "[^\w]ua="){
		set req.http.user-agent = regsub(req.url,"^.*[^\w]ua=([^&]*).*","\1");
		#set req.http.user-agent = urlcode.decode(regsub(req.url,"^.*[^\w]ua=([^&]*).*","\1"));
	}

	# do the lookup
	set req.http.dclass_openddr = dclass.classify(req.http.user-agent);
	#set req.http.dclass_openddr = dclass.classify_p(req.http.user-agent,0);
	set req.http.dclass_browser = dclass.classify_p(req.http.user-agent,1);

	# set dclass_type
	set req.http.dclass_type = "unknown";
	if(dclass.get_field("is_tablet") == "true"){
		set req.http.dclass_type = "tablet";
	} else if(dclass.get_field("is_wireless_device") == "true" && dclass.get_field("inputDevices") == "touchscreen"){
		set req.http.dclass_type = "smartphone";
	} else if(dclass.get_field("is_wireless_device") == "true"){
		set req.http.dclass_type = "phone";
	} else if(dclass.get_field("is_crawler") == "true"){
		set req.http.dclass_type = "crawler";
	} else if(dclass.get_field("is_desktop") == "true"){
		set req.http.dclass_type = "desktop";
	}

	# /uadiag commands
	if(req.url ~ "^/uadiag.js"){
		error 849 "uadiag.js";
	} else if(req.url ~ "^/uadiag"){
		error 848 "uadiag";
	}
}

# add dclass_type to the hash
sub vcl_hash {
	hash_data(req.url);

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

	hash_data(req.http.dclass_type);

	return (hash);
}

sub vcl_error {
	# plain text ua diag
	if(obj.status == 848){
		set obj.status = 200;
		set obj.http.Content-Type = "text/html; charset=utf-8";
		synthetic
{"<title>UADIAG</title>

UADIAG

User-Agent:       "} + req.http.User-Agent + {"

dClass version:   "} + dclass.get_version() + {"

dClass type:      "} + req.http.dclass_type + {"

OpenDDR id:       "} + req.http.dclass_openddr + {"
OpenDDR device:   "} + dclass.get_field("vendor") + {" "} + dclass.get_field("model") + {"
OpenDDR display:  "} + dclass.get_ifield("displayWidth") + {"x"} + dclass.get_ifield("displayHeight") + {"
OpenDDR input:    "} + dclass.get_field("inputDevices") + {"
OpenDDR js:       "} + dclass.get_field("ajax_support_javascript") + {"
OpenDDR wireless: "} + dclass.get_field("is_wireless_device") + {"
OpenDDR tablet:   "} + dclass.get_field("is_tablet") + {"
OpenDDR crawler:  "} + dclass.get_field("is_crawler") + {"
OpenDDR desktop:  "} + dclass.get_field("is_desktop") + {"

browser id:       "} + req.http.dclass_browser + {"
browser name:     "} + dclass.get_field_p("browser",1) + {"
browser ver:      "} + dclass.get_ifield_p("version",1) + {"
browser os:       "} + dclass.get_field_p("os",1) + {"

"};
		return(deliver);
	}
	# json ua diag with optional jsonp callback parameter
	else if(obj.status == 849){
		set obj.status = 200;
		set req.http.dclass_cb = "";
		set req.http.dclass_cbe = "";
		if(req.url ~ "[^\w]callback=\w+"){
			set obj.http.Content-Type = "application/javascript; charset=utf-8";
			set req.http.dclass_cb = regsub(req.url,"^.*[^\w]callback=(\w+).*","\1(");
			set req.http.dclass_cbe = ");";
		} else {
			set obj.http.Content-Type = "application/json; charset=utf-8";
		}
		synthetic
req.http.dclass_cb + {"{
"user_agent":""} + req.http.User-Agent + {"",
"dclass_version":""} + dclass.get_version() + {"",
"openddr_id":""} + req.http.dclass_openddr + {"",
"openddr_type":""} + req.http.dclass_type + {"",
"openddr_device":""} + dclass.get_field("vendor") + {" "} + dclass.get_field("model") + {"",
"openddr_display":""} + dclass.get_ifield("displayWidth") + {"x"} + dclass.get_ifield("displayHeight") + {"",
"openddr_input":""} + dclass.get_field("inputDevices") + {"",
"openddr_js":""} + dclass.get_field("ajax_support_javascript") + {"",
"openddr_wireless":""} + dclass.get_field("is_wireless_device") + {"",
"openddr_tablet":""} + dclass.get_field("is_tablet") + {"",
"openddr_crawler":""} + dclass.get_field("is_crawler") + {"",
"openddr_desktop":""} + dclass.get_field("is_desktop") + {"",
"browser_id":""} + req.http.dclass_browser + {"",
"browser_name":""} + dclass.get_field_p("browser",1) + {"",
"browser_ver":""} + dclass.get_ifield_p("version",1) + {"",
"browser_os":""} + dclass.get_field_p("os",1) + {""
}"} + req.http.dclass_cbe;
		return(deliver);
	}
}


varnish: use std.lower for compatibility with buster

# 00_early_vcl_recv.vcl
#
# Do early manglement of the host header to simplify its future handling
#
# File managed by puppet. All modifications will be lost.

import std;

sub vcl_recv {
  # Keep original Host header in X-Swh-Original-Host.
  set req.http.x-swh-original-host = req.http.host;

  # Set Host header to lower case and trim trailing port number.
  set req.http.host = regsub(std.tolower(req.http.host), ":[0-9]+$", "");
}

# 00_early_vcl_recv.vcl
#
# Do early manglement of the host header to simplify its future handling
#
# File managed by puppet. All modifications will be lost.

sub vcl_recv {
  # Keep original Host header in X-Swh-Original-Host.
  set req.http.x-swh-original-host = req.http.host;

  # Set Host header to lower case and trim trailing port number.
  set req.http.host = regsub(req.http.host.lower(), ":[0-9]+$", "");
}


Make X-Forwarded-Host compatible with Fastly shielding

if (!req.http.Fastly-FF) {
  set req.http.X-Forwarded-Host = req.http.host;
}


Fix 400 bad request of varnish with nginx and apache 2.4

Update vcl from https://github.com/cosimo/varnish-accept-language
./gen_vcl.pl pt de eo es fr hy it pt ru < accept-language.c > new.vcl

(ActionItem3186)

C{

/* ------------------------------------------------------------------ */
/* THIS FILE IS AUTOMATICALLY GENERATED BY ./gen_vcl.pl. DO NOT EDIT. */

/*
 * Accept-language header normalization
 *
 * - Parses client Accept-Language HTTP header
 * - Tries to find the best match with the supported languages
 * - Writes the best match as req.http.X-Varnish-Accept-Language
 *
 * First version: Cosimo, 21/Jan/2010
 * Last update:   Cosimo, 03/Nov/2011
 *
 * http://github.com/cosimo/varnish-accept-language
 *
 */

#include <ctype.h>  /* isupper */
#include <stdio.h>
#include <stdlib.h> /* qsort */
#include <string.h>

#define DEFAULT_LANGUAGE "pt"
#define SUPPORTED_LANGUAGES ":de:eo:es:fr:hy:it:pt:ru:"

#define vcl_string char
#define LANG_LIST_SIZE 16
#define HDR_MAXLEN 256
#define LANG_MAXLEN 8
#define RETURN_LANG(x) { \
    strncpy(lang, x, LANG_MAXLEN); \
    return; \
}
#define RETURN_DEFAULT_LANG RETURN_LANG(DEFAULT_LANGUAGE)
#define PUSH_LANG(x,y) { \
    /* fprintf(stderr, "Pushing lang [%d] %s %.4f\n", curr_lang, x, y); */ \
    /* We have to copy, otherwise root_lang will be the same every time */ \
    strncpy(pl[curr_lang].lang, x, LANG_MAXLEN); \
    pl[curr_lang].q = y;       \
    curr_lang++;               \
}

struct lang_list {
    vcl_string lang[LANG_MAXLEN];
    float q;
};

/* In-place lowercase of a string */
static void strtolower(char *s) {
    register char *c;
    for (c=s; *c; c++) {
        if (isupper(*c)) {
            *c = tolower(*c);
        }
    }
    return;
}

/* Checks if a given language is in the static list of the ones we support */
int is_supported(vcl_string *lang) {
    vcl_string *supported_languages = SUPPORTED_LANGUAGES;
    vcl_string match_str[LANG_MAXLEN + 3] = "";  /* :, :, \0 = 3 */
    int is_supported = 0;

    /* We want to match 'zh-cn' and 'zh-CN' too */
    strtolower(lang);

    /* Search ":<lang>:" in supported languages string */
    strncpy(match_str, ":", 1);
    strncat(match_str, lang, LANG_MAXLEN);
    strncat(match_str, ":\0", 2);

    if (strstr(supported_languages, match_str))
        is_supported = 1;

    return is_supported;
}

/* Used by qsort() below */
int sort_by_q(const void *x, const void *y) {
    struct lang_list *a = (struct lang_list *)x;
    struct lang_list *b = (struct lang_list *)y;
    if (a->q > b->q) return -1;
    if (a->q < b->q) return 1;
    return 0;
}

/* Reads Accept-Language, parses it, and finds the first match
   among the supported languages. In case of no match,
   returns the default language.
*/
void select_language(const vcl_string *incoming_header, char *lang) {

    struct lang_list pl[LANG_LIST_SIZE];
    vcl_string *lang_tok = NULL;
    vcl_string root_lang[3];
    vcl_string *header;
    vcl_string header_copy[HDR_MAXLEN];
    vcl_string *pos = NULL;
    vcl_string *q_spec = NULL;
    unsigned int curr_lang = 0, i = 0;
    float q;

    /* Empty or default string, return default language immediately */
    if (
        !incoming_header
        || (0 == strcmp(incoming_header, "en-US"))
        || (0 == strcmp(incoming_header, "en-GB"))
        || (0 == strcmp(incoming_header, DEFAULT_LANGUAGE))
        || (0 == strcmp(incoming_header, ""))
    )
        RETURN_DEFAULT_LANG;

    /* Tokenize Accept-Language */
    header = strncpy(header_copy, incoming_header, sizeof(header_copy));

    while ((lang_tok = strtok_r(header, " ,", &pos))) {

        q = 1.0;

        if ((q_spec = strstr(lang_tok, ";q="))) {
            /* Truncate language name before ';' */
            *q_spec = '\0';
            /* Get q value */
            sscanf(q_spec + 3, "%f", &q);
        }

        /* Wildcard language '*' should be last in list */
        if ((*lang_tok) == '*') q = 0.0;

        /* Push in the prioritized list */
        PUSH_LANG(lang_tok, q);

        /* For cases like 'en-GB', we also want the root language in the final list */
        if ('-' == lang_tok[2]) {
            root_lang[0] = lang_tok[0];
            root_lang[1] = lang_tok[1];
            root_lang[2] = '\0';
            PUSH_LANG(root_lang, q - 0.001);
        }

        /* For strtok_r() to proceed from where it left off */
        header = NULL;

        /* Break out if stored max no. of languages */
        if (curr_lang >= LANG_LIST_SIZE)
            break;
    }

    /* Sort by priority */
    qsort(pl, curr_lang, sizeof(struct lang_list), &sort_by_q);

    /* Match with supported languages */
    for (i = 0; i < curr_lang; i++) {
        if (is_supported(pl[i].lang))
            RETURN_LANG(pl[i].lang);
    }

    RETURN_DEFAULT_LANG;
}

/* Reads req.http.Accept-Language and writes X-Varnish-Accept-Language */
void vcl_rewrite_accept_language(const struct sess *sp) {
    vcl_string *in_hdr;
    vcl_string lang[LANG_MAXLEN];

    /* Get Accept-Language header from client */
    in_hdr = VRT_GetHdr(sp, HDR_REQ, "\020Accept-Language:");

    /* Normalize and filter out by list of supported languages */
    memset(lang, 0, sizeof(lang));
    select_language(in_hdr, lang);

    /* By default, use a different header name: don't mess with backend logic */
    VRT_SetHdr(sp, HDR_REQ, "\032X-Varnish-Accept-Language:", lang, vrt_magic_string_end);

    return;
}

/* vim: syn=c ts=4 et sts=4 sw=4 tw=0
*/

/* THIS FILE IS AUTOMATICALLY GENERATED BY ./gen_vcl.pl. DO NOT EDIT. */
/* ------------------------------------------------------------------ */
}C

sub vcl_recv {
  C{
    vcl_rewrite_accept_language(sp);
  }C
}

sub vcl_fetch {
  if (beresp.http.Vary) {
    set beresp.http.Vary = beresp.http.Vary + ", X-Varnish-Accept-Language";
  } else {
    set beresp.http.Vary = "X-Varnish-Accept-Language";
  }
}

C{

/* ------------------------------------------------------------------ */
/* THIS FILE IS AUTOMATICALLY GENERATED BY ./gen_vcl.pl. DO NOT EDIT. */

/*
 * Accept-language header normalization
 *
 * Cosimo, 21/01/2010
 *
 */

#include <ctype.h>  /* isupper */
#include <stdio.h>
#include <stdlib.h> /* qsort */
#include <string.h>

#define DEFAULT_LANGUAGE "en"
#define SUPPORTED_LANGUAGES ":de:fr:es:ru:pt:hy:en:"

#define vcl_string char
#define LANG_LIST_SIZE 16
#define LANG_MAXLEN 16
#define RETURN_LANG(x) { \
    strncpy(lang, x, LANG_MAXLEN); \
    return; \
}
#define RETURN_DEFAULT_LANG RETURN_LANG(DEFAULT_LANGUAGE)
#define PUSH_LANG(x,y) { \
    /* fprintf(stderr, "Pushing lang [%d] %s %.4f\n", curr_lang, x, y); */ \
    /* We have to copy, otherwise root_lang will be the same every time */ \
    strncpy(pl[curr_lang].lang, x, LANG_MAXLEN); \
    pl[curr_lang].q = y;       \
    curr_lang++;               \
}

struct lang_list {
    vcl_string lang[LANG_MAXLEN];
    float q;
};

/* In-place lowercase of a string */
static void strtolower(char *s) {
    register char *c;
    for (c=s; *c; c++) {
        if (isupper(*c)) {
            *c = tolower(*c);
        }
    }
    return;
}

/* Checks if a given language is in the static list of the ones we support */
int is_supported(vcl_string *lang) {
    vcl_string *supported_languages = SUPPORTED_LANGUAGES;
    vcl_string match_str[LANG_MAXLEN + 3] = "";  /* :, :, \0 = 3 */
    int is_supported = 0;

    /* We want to match 'zh-cn' and 'zh-CN' too */
    strtolower(lang);

    /* Search ":<lang>:" in supported languages string */
    strncpy(match_str, ":", 1);
    strncat(match_str, lang, LANG_MAXLEN);
    strncat(match_str, ":\0", 2);

    if (strstr(supported_languages, match_str)) {
        is_supported = 1;
    }

    return is_supported;
}

/* Used by qsort() below */
int sort_by_q(const void *x, const void *y) {
    struct lang_list *a = (struct lang_list *)x;
    struct lang_list *b = (struct lang_list *)y;
    if (a->q > b->q) return -1;
    if (a->q < b->q) return 1;
    return 0;
}

/* Reads Accept-Language, parses it, and finds the first match
   among the supported languages. In case of no match,
   returns the default language.
*/
void select_language(const vcl_string *incoming_header, char *lang) {

    struct lang_list pl[LANG_LIST_SIZE];
    vcl_string *lang_tok = NULL;
    vcl_string root_lang[3];
    vcl_string *header;
    vcl_string *pos = NULL;
    vcl_string *q_spec = NULL;
    unsigned int curr_lang = 0, i = 0;
    float q;

    /* Empty or default string, return default language immediately */
    if (
        !incoming_header
        || (0 == strcmp(incoming_header, "en-US"))
        || (0 == strcmp(incoming_header, "en-GB"))
        || (0 == strcmp(incoming_header, DEFAULT_LANGUAGE))
        || (0 == strcmp(incoming_header, ""))
    )
        RETURN_DEFAULT_LANG;

    /* Tokenize Accept-Language */
    header = (vcl_string *) incoming_header;

    while ((lang_tok = strtok_r(header, " ,", &pos))) {

        q = 1.0;

        if ((q_spec = strstr(lang_tok, ";q="))) {
            /* Truncate language name before ';' */
            *q_spec = '\0';
            /* Get q value */
            sscanf(q_spec + 3, "%f", &q);
        }

        /* Wildcard language '*' should be last in list */
        if ((*lang_tok) == '*') q = 0.0;

        /* Push in the prioritized list */
        PUSH_LANG(lang_tok, q);

        /* For cases like 'en-GB', we also want the root language in the final list */
        if ('-' == lang_tok[2]) {
            root_lang[0] = lang_tok[0];
            root_lang[1] = lang_tok[1];
            root_lang[2] = '\0';
            PUSH_LANG(root_lang, q - 0.001);
        }

        /* For strtok_r() to proceed from where it left off */
        header = NULL;

        /* Break out if stored max no. of languages */
        if (curr_lang >= LANG_MAXLEN) break;
    }

    /* Sort by priority */
    qsort(pl, curr_lang, sizeof(struct lang_list), &sort_by_q);

    /* Match with supported languages */
    for (i = 0; i < curr_lang; i++) {
        if (is_supported(pl[i].lang))
            RETURN_LANG(pl[i].lang);
    }

    RETURN_DEFAULT_LANG;
}

/* Reads req.http.Accept-Language and writes X-Varnish-Accept-Language */
void vcl_rewrite_accept_language(const struct sess *sp) {
    vcl_string *in_hdr;
    vcl_string lang[LANG_MAXLEN];

    memset(lang, 0, LANG_MAXLEN);

    /* Get Accept-Language header from client */
    in_hdr = VRT_GetHdr(sp, HDR_REQ, "\020Accept-Language:");

    /* Normalize and filter out by list of supported languages */
    select_language(in_hdr, lang);

    /* By default, use a different header name: don't mess with backend logic */
    VRT_SetHdr(sp, HDR_REQ, "\032X-Varnish-Accept-Language:", lang, vrt_magic_string_end);

    return;
}

/* vim: syn=c ts=4 et sts=4 sw=4 tw=0
*/

/* THIS FILE IS AUTOMATICALLY GENERATED BY ./gen_vcl.pl. DO NOT EDIT. */
/* ------------------------------------------------------------------ */
}C

sub vcl_recv {
  C{
    vcl_rewrite_accept_language(sp);
  }C
}

sub vcl_fetch {
  if (beresp.http.Vary) {
    set beresp.http.Vary = beresp.http.Vary + ", X-Varnish-Accept-Language";
  } else {
    set beresp.http.Vary = "X-Varnish-Accept-Language";
  }
}