Don't strip query string on /search?foo=bar (#1507)

sub vcl_recv {

    # I'm not 100% sure on what this is exactly for, it was taken from the
    # Fastly documentation, however, what I *believe* it does is just ensure
    # that we don't serve a stale copy of the page from the shield node when
    # an edge node is requesting content.
    if (req.http.Fastly-FF) {
        set req.max_stale_while_revalidate = 0s;
    }

    # Some (Older) clients will send a hash fragment as part of the URL even
    # though that is a local only modification. This breaks this badly for the
    # files in S3, and in general it's just not needed.
    set req.url = regsub(req.url, "#.*$", "");

    # Fastly does some normalization of the Accept-Encoding header so that it
    # reduces the number of cached copies (when served with the common,
    # Vary: Accept-Encoding) that are cached for any one URL. This makes a lot
    # of sense, except for the fact that we want to enable brotli compression
    # for our static files. Thus we need to work around the normalized encoding
    # in a way that still minimizes cached copies, but which will allow our
    # static files to be served using brotli.
    if (req.url ~ "^/static/" && req.http.Fastly-Orig-Accept-Encoding) {
        if (req.http.User-Agent ~ "MSIE 6") {
            # For that 0.3% of stubborn users out there
            unset req.http.Accept-Encoding;
        } elsif (req.http.Fastly-Orig-Accept-Encoding ~ "br") {
            set req.http.Accept-Encoding = "br";
        } elsif (req.http.Fastly-Orig-Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } else {
            unset req.http.Accept-Encoding;
        }
    }

    # Most of the URLs in Warehouse do not support or require any sort of query
    # parameter. If we strip these at the edge then we'll increase our cache
    # efficiency when they won't otherwise change the output of the pages.
    #
    # This will match any URL except those that start with:
    #
    #   * /search/
    #   * /account/login/
    #   * /account/logout/
    #   * /account/register/
    #   * /pypi
    if (req.url.path !~ "^/(search(/|$)|account/(login|logout|register)/|pypi)") {
        set req.url = req.url.path;
    }

    # Sort all of our query parameters, this will ensure that the same query
    # parameters in a different order will end up being represented as the same
    # thing, reducing cache misses due to ordering differences.
    set req.url = boltsort.sort(req.url);

#FASTLY recv

    # We want to Force SSL for the WebUI by redirecting to the HTTPS version of
    # the page, however for API calls we want to return an error code directing
    # people to instead use HTTPS.
    if (!req.http.Fastly-SSL) {

        # The /simple/ and /packages/ API.
        if (req.url ~ "^/(simple|packages)") {
            error 803 "SSL is required";
        }

        # The Legacy JSON API.
        if (req.url ~ "^/pypi/.+/json$") {
            error 803 "SSL is required";
        }

        # The Legacy ?:action= API.
        if (req.url ~ "^/pypi.*(\?|&)=:action") {
            error 803 "SSL is required";
        }

        # If we're on the /pypi page and we've received something other than a
        # GET or HEAD request, then we have no way to determine if a particular
        # request is an API call or not because it'll be in the request body
        # and that isn't available to us here. So in those cases, we won't
        # do a redirect.
        if (req.url ~ "^/pypi") {
            if (req.request == "GET" || req.request == "HEAD") {
                error 801 "Force SSL";
            }
        }
        else {
            # This isn't a /pypi URL so we'll just unconditionally redirect to
            # HTTPS.
            error 801 "Force SSL";
        }
    }

    # Redirect pypi.io, www.pypi.io, and warehouse.python.org to pypi.org, this
    # is purposely done *after* the HTTPS checks.
    if (std.tolower(req.http.host) ~ "^(www.pypi.org|(www.)?pypi.io|warehouse.python.org)$") {
        set req.http.Location = "https://pypi.org" req.url;
        error 750 "Redirect to Primary Domain";
    }
    # Redirect warehouse-staging.python.org to test.pypi.io.
    if (std.tolower(req.http.host) ~ "^(test.pypi.io|warehouse-staging.python.org)$") {
        set req.http.Location = "https://test.pypi.org" req.url;
        error 750 "Redirect to Primary Domain";
    }

    # Requests to /packages/ get dispatched to Amazon instead of to our typical
    # Origin. This requires a a bit of setup to make it work.
    if (req.http.host ~ "^(test-)?files.pythonhosted.org$"
            && req.url ~ "^/packages/[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{60}/") {
        # Setup our environment to better match what S3 expects/needs
        set req.http.Host = req.http.AWS-Bucket-Name ".s3.amazonaws.com";
        set req.http.Date = now;
        set req.url = regsuball(req.url, "\+", urlencode("+"));

        # Compute the Authorization header that S3 requires to be able to
        # access the files stored there.
        set req.http.Authorization = "AWS " req.http.AWS-Access-Key-ID ":" digest.hmac_sha1_base64(req.http.AWS-Secret-Access-Key, "GET" LF LF LF req.http.Date LF "/" req.http.AWS-Bucket-Name req.url.path);

        # We don't want to send our Warehouse-Token to S3, so we'll go ahead
        # and remove it.
        unset req.http.Warehouse-Token;
    }

    # We no longer need any of these variables, which would exist only to
    # shuffle configuration from the Fastly UI into our VCL.
    unset req.http.AWS-Access-Key-ID;
    unset req.http.AWS-Secret-Access-Key;
    unset req.http.AWS-Bucket-Name;

    # We have a number of items that we'll pass back to the origin, but only
    # if we have a Warehouse-Token that will allow them to be accepted.
    if (req.http.Warehouse-Token) {
        # Set a header to tell the backend if we're using https or http.
        if (req.http.Fastly-SSL) {
            set req.http.Warehouse-Proto = "https";
        } else {
            set req.http.Warehouse-Proto = "http";
        }

        # Pass the client IP address back to the backend.
        if (req.http.Fastly-Client-IP) {
            set req.http.Warehouse-IP = req.http.Fastly-Client-IP;
        }

        # Pass the real host value back to the backend.
        if (req.http.Host) {
            set req.http.Warehouse-Host = req.http.host;
        }
    }

    # On a POST, we want to skip the shielding and hit backends directly.
    if (req.request == "POST") {
        set req.backend = F_Heroku;
    }

    # Do not bother to attempt to run the caching mechanisms for methods that
    # are not generally safe to cache.
    if (req.request != "HEAD" &&
        req.request != "GET" &&
        req.request != "FASTLYPURGE") {
      return(pass);
    }

    # We don't ever want to cache our health URL. Outside systems should be
    # able to use it to reach past Fastly and get an end to end health check.
    if (req.url == "/_health/") {
        return(pass);
    }

    # Finally, return the default lookup action.
    return(lookup);
}

sub vcl_fetch {

    # These are newer kinds of redirects which should be able to be cached by
    # default, even though Fastly doesn't currently have them in their default
    # list of cacheable status codes.
    if (http_status_matches(beresp.status, "303,307,308")) {
        set beresp.cacheable = true;
    }

    # For any 5xx status code we want to see if a stale object exists for it,
    # if so we'll go ahead and serve it.
    if (beresp.status >= 500 && beresp.status < 600) {
        if (stale.exists) {
            return(deliver_stale);
        }
    }

    # When delivering a 304 response, we don't always have access to all the
    # headers in the resp because a 304 response is supposed to remove most of
    # the headers. So we'll instead stash these headers on the request so that
    # we can log this data from there instead of from the response.
    if (beresp.http.x-amz-meta-project
            || beresp.http.x-amz-meta-version
            || beresp.http.x-amz-meta-package-type) {
        set req.http.Fastly-amz-meta-project = beresp.http.x-amz-meta-project;
        set req.http.Fastly-amz-meta-version = beresp.http.x-amz-meta-version;
        set req.http.Fastly-amz-meta-package-type = beresp.http.x-amz-meta-package-type;
    }

#FASTLY fetch

    # Trigger a "SSL is required" error if the backend has indicated to do so.
    if (beresp.http.X-Fastly-Error == "803") {
        error 803 "SSL is required";
    }

    # If we've gotten a 502 or a 503 from the backend, we'll go ahead and retry
    # the request.
    if ((beresp.status == 502 || beresp.status == 503) &&
            req.restarts < 1 &&
            (req.request == "GET" || req.request == "HEAD")) {
        restart;
    }

    # If we've restarted, then we'll record the number of restarts.
    if(req.restarts > 0 ) {
        set beresp.http.Fastly-Restarts = req.restarts;
    }

    # If there is a Set-Cookie header, we'll ensure that we do not cache the
    # response.
    if (beresp.http.Set-Cookie) {
        set req.http.Fastly-Cachetype = "SETCOOKIE";
        return (pass);
    }

    # If the response has the private Cache-Control directive then we won't
    # cache it.
    if (beresp.http.Cache-Control ~ "private") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

    # If we've gotten an error after the restarts we'll deliver the response
    # with a very short cache time.
    if (http_status_matches(beresp.status, "500,502,503")) {
        set req.http.Fastly-Cachetype = "ERROR";
        set beresp.ttl = 1s;
        set beresp.grace = 5s;
        return (deliver);
    }

    # Apply a default TTL if there isn't a max-age or s-maxage.
    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 = 60s;
    }

    # Actually deliver the fetched response.
    return(deliver);
}

sub vcl_hit {
#FASTLY hit

    # If the object we have isn't cacheable, then just serve it directly
    # without going through any of the caching mechanisms.
    if (!obj.cacheable) {
        return(pass);
    }

    return(deliver);
}

sub vcl_deliver {
    # If this is an error and we have a stale response available, restart so
    # that we can pick it up and serve it.
    if (resp.status >= 500 && resp.status < 600) {
        if (stale.exists) {
            restart;
        }
    }

#FASTLY deliver

    # Unset headers that we don't need/want to send on to the client because
    # they are not generally useful.
    unset resp.http.Server;
    unset resp.http.Via;

    # Unset a few headers set by Amazon that we don't really have a need/desire
    # to send to clients.
    unset resp.http.X-AMZ-Replication-Status;
    unset resp.http.X-AMZ-Meta-Python-Version;
    unset resp.http.X-AMZ-Meta-Version;
    unset resp.http.X-AMZ-Meta-Package-Type;
    unset resp.http.X-AMZ-Meta-Project;

    # Set our standard security headers, we do this in VCL rather than in
    # Warehouse itself so that we always get these headers, regardless of the
    # origin server being used.
    set resp.http.Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload";
    set resp.http.X-Frame-Options = "deny";
    set resp.http.X-XSS-Protection = "1; mode=block";
    set resp.http.X-Content-Type-Options = "nosniff";
    set resp.http.X-Permitted-Cross-Domain-Policies = "none";

    # Unstash our information about what project/version/package-type a
    # particular file download was for.
    if (req.http.Fastly-amz-meta-project
            || req.http.Fastly-amz-meta-version
            || req.http.Fastly-amz-meta-package-type) {
        set resp.http.x-amz-meta-project = req.http.Fastly-amz-meta-project;
        set resp.http.x-amz-meta-version = req.http.Fastly-amz-meta-version;
        set resp.http.x-amz-meta-package-type = req.http.Fastly-amz-meta-package-type;
    }

    # If we're not executing a shielding request, and the URL is one of our file
    # URLs, and it's a GET request, and the response is either a 200 or a 304
    # then we want to log an event stating that a download has taken place.
    if (!req.http.Fastly-FF
            && std.tolower(req.http.host) == "files.pythonhosted.org"
            && req.url.path ~ "^/packages/[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{60}/"
            && req.request == "GET"
            && http_status_matches(resp.status, "200,304")) {
        log {"syslog "} req.service_id {" linehaul :: "} "2@" now "|" geoip.country_code "|" req.url.path "|" tls.client.protocol "|" tls.client.cipher "|" resp.http.x-amz-meta-project "|" resp.http.x-amz-meta-version "|" resp.http.x-amz-meta-package-type "|" req.http.user-agent;
        log {"syslog "} req.service_id {" downloads :: "} "2@" now "|" geoip.country_code "|" req.url.path "|" tls.client.protocol "|" tls.client.cipher "|" resp.http.x-amz-meta-project "|" resp.http.x-amz-meta-version "|" resp.http.x-amz-meta-package-type "|" req.http.user-agent;
    }

    return(deliver);
}

sub vcl_error {
#FASTLY error

    # If we have a 5xx error and there is a stale object available, then we
    # will deliver that stale object.
    if (obj.status >= 500 && obj.status < 600) {
        if (stale.exists) {
            return(deliver_stale);
        }
    }

    if (obj.status == 803) {
        set obj.status = 403;
        set obj.response = "SSL is required";
        set obj.http.Content-Type = "text/plain; charset=UTF-8";
        synthetic {"SSL is required."};
        return (deliver);
    } else if (obj.status == 750) {
        set obj.status = 301;
        set obj.http.Location = req.http.Location;
        set obj.http.Content-Type = "text/html; charset=UTF-8";
        synthetic {"<title>301 Moved Permanently</title>
301 Moved Permanently
"};
        return(deliver);
    }

}

sub vcl_recv {

    # I'm not 100% sure on what this is exactly for, it was taken from the
    # Fastly documentation, however, what I *believe* it does is just ensure
    # that we don't serve a stale copy of the page from the shield node when
    # an edge node is requesting content.
    if (req.http.Fastly-FF) {
        set req.max_stale_while_revalidate = 0s;
    }

    # Some (Older) clients will send a hash fragment as part of the URL even
    # though that is a local only modification. This breaks this badly for the
    # files in S3, and in general it's just not needed.
    set req.url = regsub(req.url, "#.*$", "");

    # Fastly does some normalization of the Accept-Encoding header so that it
    # reduces the number of cached copies (when served with the common,
    # Vary: Accept-Encoding) that are cached for any one URL. This makes a lot
    # of sense, except for the fact that we want to enable brotli compression
    # for our static files. Thus we need to work around the normalized encoding
    # in a way that still minimizes cached copies, but which will allow our
    # static files to be served using brotli.
    if (req.url ~ "^/static/" && req.http.Fastly-Orig-Accept-Encoding) {
        if (req.http.User-Agent ~ "MSIE 6") {
            # For that 0.3% of stubborn users out there
            unset req.http.Accept-Encoding;
        } elsif (req.http.Fastly-Orig-Accept-Encoding ~ "br") {
            set req.http.Accept-Encoding = "br";
        } elsif (req.http.Fastly-Orig-Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } else {
            unset req.http.Accept-Encoding;
        }
    }

    # Most of the URLs in Warehouse do not support or require any sort of query
    # parameter. If we strip these at the edge then we'll increase our cache
    # efficiency when they won't otherwise change the output of the pages.
    #
    # This will match any URL except those that start with:
    #
    #   * /search/
    #   * /account/login/
    #   * /account/logout/
    #   * /account/register/
    #   * /pypi
    if (req.url !~ "^/(search/|account/(login|logout|register)/|pypi)") {
        set req.url = req.url.path;
    }

    # Sort all of our query parameters, this will ensure that the same query
    # parameters in a different order will end up being represented as the same
    # thing, reducing cache misses due to ordering differences.
    set req.url = boltsort.sort(req.url);

#FASTLY recv

    # We want to Force SSL for the WebUI by redirecting to the HTTPS version of
    # the page, however for API calls we want to return an error code directing
    # people to instead use HTTPS.
    if (!req.http.Fastly-SSL) {

        # The /simple/ and /packages/ API.
        if (req.url ~ "^/(simple|packages)") {
            error 803 "SSL is required";
        }

        # The Legacy JSON API.
        if (req.url ~ "^/pypi/.+/json$") {
            error 803 "SSL is required";
        }

        # The Legacy ?:action= API.
        if (req.url ~ "^/pypi.*(\?|&)=:action") {
            error 803 "SSL is required";
        }

        # If we're on the /pypi page and we've received something other than a
        # GET or HEAD request, then we have no way to determine if a particular
        # request is an API call or not because it'll be in the request body
        # and that isn't available to us here. So in those cases, we won't
        # do a redirect.
        if (req.url ~ "^/pypi") {
            if (req.request == "GET" || req.request == "HEAD") {
                error 801 "Force SSL";
            }
        }
        else {
            # This isn't a /pypi URL so we'll just unconditionally redirect to
            # HTTPS.
            error 801 "Force SSL";
        }
    }

    # Redirect pypi.io, www.pypi.io, and warehouse.python.org to pypi.org, this
    # is purposely done *after* the HTTPS checks.
    if (std.tolower(req.http.host) ~ "^(www.pypi.org|(www.)?pypi.io|warehouse.python.org)$") {
        set req.http.Location = "https://pypi.org" req.url;
        error 750 "Redirect to Primary Domain";
    }
    # Redirect warehouse-staging.python.org to test.pypi.io.
    if (std.tolower(req.http.host) ~ "^(test.pypi.io|warehouse-staging.python.org)$") {
        set req.http.Location = "https://test.pypi.org" req.url;
        error 750 "Redirect to Primary Domain";
    }

    # Requests to /packages/ get dispatched to Amazon instead of to our typical
    # Origin. This requires a a bit of setup to make it work.
    if (req.http.host ~ "^(test-)?files.pythonhosted.org$"
            && req.url ~ "^/packages/[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{60}/") {
        # Setup our environment to better match what S3 expects/needs
        set req.http.Host = req.http.AWS-Bucket-Name ".s3.amazonaws.com";
        set req.http.Date = now;
        set req.url = regsuball(req.url, "\+", urlencode("+"));

        # Compute the Authorization header that S3 requires to be able to
        # access the files stored there.
        set req.http.Authorization = "AWS " req.http.AWS-Access-Key-ID ":" digest.hmac_sha1_base64(req.http.AWS-Secret-Access-Key, "GET" LF LF LF req.http.Date LF "/" req.http.AWS-Bucket-Name req.url.path);

        # We don't want to send our Warehouse-Token to S3, so we'll go ahead
        # and remove it.
        unset req.http.Warehouse-Token;
    }

    # We no longer need any of these variables, which would exist only to
    # shuffle configuration from the Fastly UI into our VCL.
    unset req.http.AWS-Access-Key-ID;
    unset req.http.AWS-Secret-Access-Key;
    unset req.http.AWS-Bucket-Name;

    # We have a number of items that we'll pass back to the origin, but only
    # if we have a Warehouse-Token that will allow them to be accepted.
    if (req.http.Warehouse-Token) {
        # Set a header to tell the backend if we're using https or http.
        if (req.http.Fastly-SSL) {
            set req.http.Warehouse-Proto = "https";
        } else {
            set req.http.Warehouse-Proto = "http";
        }

        # Pass the client IP address back to the backend.
        if (req.http.Fastly-Client-IP) {
            set req.http.Warehouse-IP = req.http.Fastly-Client-IP;
        }

        # Pass the real host value back to the backend.
        if (req.http.Host) {
            set req.http.Warehouse-Host = req.http.host;
        }
    }

    # On a POST, we want to skip the shielding and hit backends directly.
    if (req.request == "POST") {
        set req.backend = F_Heroku;
    }

    # Do not bother to attempt to run the caching mechanisms for methods that
    # are not generally safe to cache.
    if (req.request != "HEAD" &&
        req.request != "GET" &&
        req.request != "FASTLYPURGE") {
      return(pass);
    }

    # We don't ever want to cache our health URL. Outside systems should be
    # able to use it to reach past Fastly and get an end to end health check.
    if (req.url == "/_health/") {
        return(pass);
    }

    # Finally, return the default lookup action.
    return(lookup);
}

sub vcl_fetch {

    # These are newer kinds of redirects which should be able to be cached by
    # default, even though Fastly doesn't currently have them in their default
    # list of cacheable status codes.
    if (http_status_matches(beresp.status, "303,307,308")) {
        set beresp.cacheable = true;
    }

    # For any 5xx status code we want to see if a stale object exists for it,
    # if so we'll go ahead and serve it.
    if (beresp.status >= 500 && beresp.status < 600) {
        if (stale.exists) {
            return(deliver_stale);
        }
    }

    # When delivering a 304 response, we don't always have access to all the
    # headers in the resp because a 304 response is supposed to remove most of
    # the headers. So we'll instead stash these headers on the request so that
    # we can log this data from there instead of from the response.
    if (beresp.http.x-amz-meta-project
            || beresp.http.x-amz-meta-version
            || beresp.http.x-amz-meta-package-type) {
        set req.http.Fastly-amz-meta-project = beresp.http.x-amz-meta-project;
        set req.http.Fastly-amz-meta-version = beresp.http.x-amz-meta-version;
        set req.http.Fastly-amz-meta-package-type = beresp.http.x-amz-meta-package-type;
    }

#FASTLY fetch

    # Trigger a "SSL is required" error if the backend has indicated to do so.
    if (beresp.http.X-Fastly-Error == "803") {
        error 803 "SSL is required";
    }

    # If we've gotten a 502 or a 503 from the backend, we'll go ahead and retry
    # the request.
    if ((beresp.status == 502 || beresp.status == 503) &&
            req.restarts < 1 &&
            (req.request == "GET" || req.request == "HEAD")) {
        restart;
    }

    # If we've restarted, then we'll record the number of restarts.
    if(req.restarts > 0 ) {
        set beresp.http.Fastly-Restarts = req.restarts;
    }

    # If there is a Set-Cookie header, we'll ensure that we do not cache the
    # response.
    if (beresp.http.Set-Cookie) {
        set req.http.Fastly-Cachetype = "SETCOOKIE";
        return (pass);
    }

    # If the response has the private Cache-Control directive then we won't
    # cache it.
    if (beresp.http.Cache-Control ~ "private") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

    # If we've gotten an error after the restarts we'll deliver the response
    # with a very short cache time.
    if (http_status_matches(beresp.status, "500,502,503")) {
        set req.http.Fastly-Cachetype = "ERROR";
        set beresp.ttl = 1s;
        set beresp.grace = 5s;
        return (deliver);
    }

    # Apply a default TTL if there isn't a max-age or s-maxage.
    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 = 60s;
    }

    # Actually deliver the fetched response.
    return(deliver);
}

sub vcl_hit {
#FASTLY hit

    # If the object we have isn't cacheable, then just serve it directly
    # without going through any of the caching mechanisms.
    if (!obj.cacheable) {
        return(pass);
    }

    return(deliver);
}

sub vcl_deliver {
    # If this is an error and we have a stale response available, restart so
    # that we can pick it up and serve it.
    if (resp.status >= 500 && resp.status < 600) {
        if (stale.exists) {
            restart;
        }
    }

#FASTLY deliver

    # Unset headers that we don't need/want to send on to the client because
    # they are not generally useful.
    unset resp.http.Server;
    unset resp.http.Via;

    # Unset a few headers set by Amazon that we don't really have a need/desire
    # to send to clients.
    unset resp.http.X-AMZ-Replication-Status;
    unset resp.http.X-AMZ-Meta-Python-Version;
    unset resp.http.X-AMZ-Meta-Version;
    unset resp.http.X-AMZ-Meta-Package-Type;
    unset resp.http.X-AMZ-Meta-Project;

    # Set our standard security headers, we do this in VCL rather than in
    # Warehouse itself so that we always get these headers, regardless of the
    # origin server being used.
    set resp.http.Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload";
    set resp.http.X-Frame-Options = "deny";
    set resp.http.X-XSS-Protection = "1; mode=block";
    set resp.http.X-Content-Type-Options = "nosniff";
    set resp.http.X-Permitted-Cross-Domain-Policies = "none";

    # Unstash our information about what project/version/package-type a
    # particular file download was for.
    if (req.http.Fastly-amz-meta-project
            || req.http.Fastly-amz-meta-version
            || req.http.Fastly-amz-meta-package-type) {
        set resp.http.x-amz-meta-project = req.http.Fastly-amz-meta-project;
        set resp.http.x-amz-meta-version = req.http.Fastly-amz-meta-version;
        set resp.http.x-amz-meta-package-type = req.http.Fastly-amz-meta-package-type;
    }

    # If we're not executing a shielding request, and the URL is one of our file
    # URLs, and it's a GET request, and the response is either a 200 or a 304
    # then we want to log an event stating that a download has taken place.
    if (!req.http.Fastly-FF
            && std.tolower(req.http.host) == "files.pythonhosted.org"
            && req.url.path ~ "^/packages/[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{60}/"
            && req.request == "GET"
            && http_status_matches(resp.status, "200,304")) {
        log {"syslog "} req.service_id {" linehaul :: "} "2@" now "|" geoip.country_code "|" req.url.path "|" tls.client.protocol "|" tls.client.cipher "|" resp.http.x-amz-meta-project "|" resp.http.x-amz-meta-version "|" resp.http.x-amz-meta-package-type "|" req.http.user-agent;
        log {"syslog "} req.service_id {" downloads :: "} "2@" now "|" geoip.country_code "|" req.url.path "|" tls.client.protocol "|" tls.client.cipher "|" resp.http.x-amz-meta-project "|" resp.http.x-amz-meta-version "|" resp.http.x-amz-meta-package-type "|" req.http.user-agent;
    }

    return(deliver);
}

sub vcl_error {
#FASTLY error

    # If we have a 5xx error and there is a stale object available, then we
    # will deliver that stale object.
    if (obj.status >= 500 && obj.status < 600) {
        if (stale.exists) {
            return(deliver_stale);
        }
    }

    if (obj.status == 803) {
        set obj.status = 403;
        set obj.response = "SSL is required";
        set obj.http.Content-Type = "text/plain; charset=UTF-8";
        synthetic {"SSL is required."};
        return (deliver);
    } else if (obj.status == 750) {
        set obj.status = 301;
        set obj.http.Location = req.http.Location;
        set obj.http.Content-Type = "text/html; charset=UTF-8";
        synthetic {"<title>301 Moved Permanently</title>
301 Moved Permanently
"};
        return(deliver);
    }

}


Indent according to Varnish coding standards

Thanks to Sergey Syrota for pointing this out.

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

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

    if (req.http.User-Agent ~ "(?i)(ads|google|bing|msn|yandex|baidu|ro|career|)bot" ||
	req.http.User-Agent ~ "(?i)(baidu|jike|symantec)spider" ||
	req.http.User-Agent ~ "(?i)scanner" ||
	req.http.User-Agent ~ "(?i)(web)crawler") {
	set req.http.X-UA-Device = "bot"; }
    elsif (req.http.User-Agent ~ "(?i)ip(hone|od)") { set req.http.X-UA-Device = "mobile-iphone"; }
    elsif (req.http.User-Agent ~ "(?i)ipad")        { set req.http.X-UA-Device = "tablet-ipad"; }
    # how do we differ between an android phone and an android tablet?
    # http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent
    elsif (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") { set req.http.X-UA-Device = "mobile-android"; }
    # android 3/honeycomb was just about tablet-only, and any phones will probably handle a bigger page layout.
    elsif (req.http.User-Agent ~ "(?i)android 3")              { set req.http.X-UA-Device = "tablet-android"; }
    # may very well give false positives towards android tablets. Suggestions welcome.
    elsif (req.http.User-Agent ~ "(?i)android")         { set req.http.X-UA-Device = "tablet-android"; }

    elsif (req.http.User-Agent ~ "^HTC" ||
        req.http.User-Agent ~ "Fennec" ||
        req.http.User-Agent ~ "IEMobile" ||
        req.http.User-Agent ~ "BlackBerry" ||
        req.http.User-Agent ~ "SymbianOS.*AppleWebKit" ||
        req.http.User-Agent ~ "Opera Mobi") {
        set req.http.X-UA-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)symbian" ||
        req.http.User-Agent ~ "(?i)^sonyericsson" ||
        req.http.User-Agent ~ "(?i)^nokia" ||
        req.http.User-Agent ~ "(?i)^samsung" ||
        req.http.User-Agent ~ "(?i)^lg" ||
	req.http.User-Agent ~ "(?i)bada" ||
	req.http.User-Agent ~ "(?i)blazer" ||
	req.http.User-Agent ~ "(?i)cellphone" ||
	req.http.User-Agent ~ "(?i)iemobile" ||
	req.http.User-Agent ~ "(?i)midp-2.0" ||
	req.http.User-Agent ~ "(?i)u990" ||
	req.http.User-Agent ~ "(?i)netfront" ||
	req.http.User-Agent ~ "(?i)opera mini" ||
	req.http.User-Agent ~ "(?i)palm" ||
	req.http.User-Agent ~ "(?i)nintendo wii" ||
	req.http.User-Agent ~ "(?i)playstation portable" ||
	req.http.User-Agent ~ "(?i)portalmmm" ||
	req.http.User-Agent ~ "(?i)proxinet" ||
	req.http.User-Agent ~ "(?i)sonyericsson" ||
	req.http.User-Agent ~ "(?i)symbian" ||
	req.http.User-Agent ~ "(?i)windows\ ?ce" ||
	req.http.User-Agent ~ "(?i)winwap" ||
	req.http.User-Agent ~ "(?i)eudoraweb" ||
	req.http.User-Agent ~ "(?i)htc" ||
	req.http.User-Agent ~ "(?i)240x320" ||
	req.http.User-Agent ~ "(?i)avantgo") {
        set req.http.X-UA-Device = "mobile-generic";
    }
    # handle overrides
    if (req.http.Cookie ~ "(i?)X-UA-Device-force") {
        # ;?? means zero or one ;, non-greedy to match the first.
        set req.http.X-UA-Device = regsub(req.http.Cookie, "(?i).*X-UA-Device-force=([^;]+);??.*", "\1");
        # Clean up the cookie header to allow for caching.
        set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *X-UA-Device-force=[^;]+;? *", "\1");
        if (req.http.Cookie ~ "^ *$") { unset req.http.Cookie; }
    }
}

# vim: sw=4:tw=120


give the hash to all requests

give the hash to all requests

sub set_backend {
	# Calculate the ideal region to route the request to.
	declare local var.region STRING;
	if (server.region ~ "(APAC|Asia|North-America|South-America|US-Central|US-East|US-West)") {
		set var.region = "US";
	} else {
		set var.region = "EU";
	}

	# Gather the health of the shields and origins.
	declare local var.v3_eu_is_healthy BOOL;
	set req.backend = F_v3_eu;
	set var.v3_eu_is_healthy = req.backend.healthy;

	declare local var.v3_us_is_healthy BOOL;
	set req.backend = F_v3_us;
	set var.v3_us_is_healthy = req.backend.healthy;

	# Set some sort of default, that shouldn't get used.
	set req.backend = F_v3_eu;

	# Route EU requests to the nearest healthy shield or origin.
	if (var.region == "EU") {
		if (var.v3_eu_is_healthy) {
			set req.backend = F_v3_eu;
		} elseif (var.v3_us_is_healthy) {
			set req.backend = F_v3_us;
		} else {
			# Everything is on fire... but lets try the origin anyway just in case
			# it's the probes that are wrong
			# set req.backend = F_origin_last_ditch_eu;
		}
	}

	# Route US requests to the nearest healthy shield or origin.
	if (var.region == "US") {
		if (var.v3_us_is_healthy) {
			set req.backend = F_v3_us;
		} elseif (var.v3_eu_is_healthy) {
			set req.backend = F_v3_eu;
		} else {
			# Everything is on fire... but lets try the origin anyway just in case
			# it's the probes that are wrong
			# set req.backend = F_origin_last_ditch_us;
		}
	}

	# Persist the decision so we can debug the result.
	set req.http.Debug-Backend = req.backend;

	# The Fastly macro is inserted after the backend is selected because the
	# macro has the code to select the correct req.http.Host value based on the backend.
	#FASTLY recv
}

sub vcl_recv {
	# Run this VCL only if the path is not to the polyfill-service v2 server
	if ((req.http.Orig-URL ~ "^/v3/") || req.http.Orig-URL ~ "^(?!/v2).+" && req.http.Orig-URL ~ "^(?!/__health).+" && req.http.Orig-URL ~ "^(?!/test/libs/proclaim/proclaim.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.css).+" && req.http.Orig-URL ~ "^(?!/__gtg).+") {
		if (req.http.Fastly-Debug) {
			call breadcrumb_recv;
		}

		if (req.method != "GET" && req.method != "HEAD" && req.method != "OPTIONS") {
			error 911;
		}

		if (req.method == "OPTIONS") {
 			error 912;
		}

		if (req.http.Orig-URL ~ "^/v3/polyfill(\.min)?\.js") {
			call normalise_querystring_parameters_for_polyfill_bundle;
		}

		if (req.http.Orig-URL ~ "^/test/(director|tests?)/?") {
			declare local var.ua STRING;
			set var.ua = urlencode(urldecode(subfield(req.url.qs, "ua", "&")));
			if (var.ua == "") {
				call normalise_user_agent;
				set req.url = querystring.set(req.url, "ua", req.http.Normalized-User-Agent);
			}
		}

		# Sort the querystring parameters alphabetically to improve chances of hitting a cached copy.
		# If querystring is empty, remove the ? from the url.
		set req.url = querystring.clean(querystring.sort(req.url));
		call set_backend;
	}
}

sub vcl_hash {
	if (req.http.Fastly-Debug) {
		call breadcrumb_hash;
	}

	# We are not adding req.http.host to the hash because we want https://cdn.polyfill.io and https://polyfill.io to be a single object in the cache.
	# set req.hash += req.http.host;
	set req.hash += req.url;
	# We include return(hash) to stop the function falling through to the default VCL built into varnish, which for vcl_hash will add req.url and req.http.Host to the hash.
	return(hash);
}

sub vcl_miss {
	# Run this VCL only if the path is not to the polyfill-service v2 server
	if ((req.http.Orig-URL ~ "^/v3/") || req.http.Orig-URL ~ "^(?!/v2).+" && req.http.Orig-URL ~ "^(?!/__health).+" && req.http.Orig-URL ~ "^(?!/test/libs/proclaim/proclaim.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.css).+" && req.http.Orig-URL ~ "^(?!/__gtg).+") {
		if (req.http.Fastly-Debug) {
			call breadcrumb_miss;
		}
	}
}

sub vcl_pass {
	# Run this VCL only if the path is not to the polyfill-service v2 server
	if ((req.http.Orig-URL ~ "^/v3/") || req.http.Orig-URL ~ "^(?!/v2).+" && req.http.Orig-URL ~ "^(?!/__health).+" && req.http.Orig-URL ~ "^(?!/test/libs/proclaim/proclaim.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.css).+" && req.http.Orig-URL ~ "^(?!/__gtg).+") {
		if (req.http.Fastly-Debug) {
			call breadcrumb_pass;
		}
	}
}

sub vcl_fetch {
	# Run this VCL only if the path is not to the polyfill-service v2 server
	if ((req.http.Orig-URL ~ "^/v3/") || req.http.Orig-URL ~ "^(?!/v2).+" && req.http.Orig-URL ~ "^(?!/__health).+" && req.http.Orig-URL ~ "^(?!/test/libs/proclaim/proclaim.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.css).+" && req.http.Orig-URL ~ "^(?!/__gtg).+") {
		if (req.http.Fastly-Debug) {
			call breadcrumb_fetch;
		}

		# Ensure the site is only served over HTTPS and reduce the chances of someone performing a MITM attack.
		set beresp.http.Strict-Transport-Security = "max-age=31536000; includeSubdomains; preload";
		# Enables the cross-site scripting filter built into most modern web browsers.
		set beresp.http.X-XSS-Protection = "1; mode=block";
		# Prevents MIME-sniffing a response away from the declared content type.
		set beresp.http.X-Content-Type-Options = "nosniff";

		# Allow only content from the site's own origin (this excludes subdomains.)
		# Don't allow the website to be used within an iframe
		if (!beresp.http.Content-Security-Policy) {
			set beresp.http.Content-Security-Policy = "default-src 'self'; font-src 'self' https://www.ft.com; img-src 'self' https://www.ft.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'";
		}

		# The Referrer-Policy header governs which referrer information, sent in the Referer header, should be included with requests made.
		# Send a full URL when performing a same-origin request, but only send the origin of the document for other cases.
		set beresp.http.Referrer-Policy = "origin-when-cross-origin";

		# Enable purging of all objects in the Fastly cache by issuing a purge with the key "polyfill-service".
		if (beresp.http.Surrogate-Key !~ "\bpolyfill-service\b") {
            set beresp.http.Surrogate-Key = if(beresp.http.Surrogate-Key, beresp.http.Surrogate-Key " polyfill-service", "polyfill-service");
        }

		set beresp.http.Timing-Allow-Origin = "*";

		set beresp.http.Normalized-User-Agent = req.http.Normalized-User-Agent;
		set beresp.http.Detected-User-Agent = req.http.useragent_parser_family "/"  req.http.useragent_parser_major "." req.http.useragent_parser_minor "." req.http.useragent_parser_patch;
	}
}

sub vcl_deliver {
	# Run this VCL only if the path is not to the polyfill-service v2 server
	if ((req.http.Orig-URL ~ "^/v3/") || req.http.Orig-URL ~ "^(?!/v2).+" && req.http.Orig-URL ~ "^(?!/__health).+" && req.http.Orig-URL ~ "^(?!/test/libs/proclaim/proclaim.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.css).+" && req.http.Orig-URL ~ "^(?!/__gtg).+") {
		if (req.http.Fastly-Debug) {
			call breadcrumb_deliver;
		}

		set req.http.Fastly-Force-Shield = "yes";

		# Allow cross origin GET, HEAD, and OPTIONS requests to be made.
		if (req.http.Origin) {
			set resp.http.Access-Control-Allow-Origin = "*";
			set resp.http.Access-Control-Allow-Methods = "GET,HEAD,OPTIONS";
		}

		add resp.http.Server-Timing = fastly_info.state {", fastly;desc="Edge time";dur="} time.elapsed.msec;

		if (req.http.Fastly-Debug) {
			set resp.http.Debug-Backend = req.http.Debug-Backend;
			set resp.http.Debug-Host = req.http.Host;
			set resp.http.Debug-Fastly-Restarts = req.restarts;
			set resp.http.Debug-URL = req.url;
			set resp.http.Debug-Orig-URL = req.http.Orig-URL;
			set resp.http.Debug-VCL-Route = req.http.X-VCL-Route;
			set resp.http.useragent_parser_family = req.http.useragent_parser_family;
			set resp.http.useragent_parser_major = req.http.useragent_parser_major;
			set resp.http.useragent_parser_minor = req.http.useragent_parser_minor;
			set resp.http.useragent_parser_patch = req.http.useragent_parser_patch;
		} else {
			unset resp.http.Server;
			unset resp.http.Via;
			unset resp.http.X-Cache;
			unset resp.http.X-Cache-Hits;
			unset resp.http.X-Served-By;
			unset resp.http.X-Timer;
			unset resp.http.Fastly-Restarts;
		}
	}
}

sub vcl_error {
	if (obj.status == 911) {
		set obj.status = 405;
		set obj.response = "METHOD NOT ALLOWED";
		set obj.http.Content-Type = "text/html; charset=utf-8";
		set obj.http.Cache-Control = "private, no-store";
		synthetic req.method " METHOD NOT ALLOWED";
		return (deliver);
	}
	if (obj.status == 912) {
		set obj.status = 200;
		set obj.response = "OK";
		set obj.http.Allow = "OPTIONS, GET, HEAD";
		return (deliver);
	}
}

sub set_backend {
	# Calculate the ideal region to route the request to.
	declare local var.region STRING;
	if (server.region ~ "(APAC|Asia|North-America|South-America|US-Central|US-East|US-West)") {
		set var.region = "US";
	} else {
		set var.region = "EU";
	}

	# Gather the health of the shields and origins.
	declare local var.v3_eu_is_healthy BOOL;
	set req.backend = F_v3_eu;
	set var.v3_eu_is_healthy = req.backend.healthy;

	declare local var.v3_us_is_healthy BOOL;
	set req.backend = F_v3_us;
	set var.v3_us_is_healthy = req.backend.healthy;

	# Set some sort of default, that shouldn't get used.
	set req.backend = F_v3_eu;

	# Route EU requests to the nearest healthy shield or origin.
	if (var.region == "EU") {
		if (var.v3_eu_is_healthy) {
			set req.backend = F_v3_eu;
		} elseif (var.v3_us_is_healthy) {
			set req.backend = F_v3_us;
		} else {
			# Everything is on fire... but lets try the origin anyway just in case
			# it's the probes that are wrong
			# set req.backend = F_origin_last_ditch_eu;
		}
	}

	# Route US requests to the nearest healthy shield or origin.
	if (var.region == "US") {
		if (var.v3_us_is_healthy) {
			set req.backend = F_v3_us;
		} elseif (var.v3_eu_is_healthy) {
			set req.backend = F_v3_eu;
		} else {
			# Everything is on fire... but lets try the origin anyway just in case
			# it's the probes that are wrong
			# set req.backend = F_origin_last_ditch_us;
		}
	}

	# Persist the decision so we can debug the result.
	set req.http.Debug-Backend = req.backend;

	# The Fastly macro is inserted after the backend is selected because the
	# macro has the code to select the correct req.http.Host value based on the backend.
	#FASTLY recv
}

sub vcl_recv {
	# Run this VCL only if the path is not to the polyfill-service v2 server
	if ((req.http.Orig-URL ~ "^/v3/") || req.http.Orig-URL ~ "^(?!/v2).+" && req.http.Orig-URL ~ "^(?!/__health).+" && req.http.Orig-URL ~ "^(?!/test/libs/proclaim/proclaim.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.css).+" && req.http.Orig-URL ~ "^(?!/__gtg).+") {
		if (req.http.Fastly-Debug) {
			call breadcrumb_recv;
		}

		if (req.method != "GET" && req.method != "HEAD" && req.method != "OPTIONS") {
			error 911;
		}

		if (req.method == "OPTIONS") {
 			error 912;
		}

		if (req.http.Orig-URL ~ "^/v3/polyfill(\.min)?\.js") {
			call normalise_querystring_parameters_for_polyfill_bundle;
		}

		if (req.http.Orig-URL ~ "^/test/(director|tests?)/?") {
			declare local var.ua STRING;
			set var.ua = urlencode(urldecode(subfield(req.url.qs, "ua", "&")));
			if (var.ua == "") {
				call normalise_user_agent;
				set req.url = querystring.set(req.url, "ua", req.http.Normalized-User-Agent);
			}
		}

		# Sort the querystring parameters alphabetically to improve chances of hitting a cached copy.
		# If querystring is empty, remove the ? from the url.
		set req.url = querystring.clean(querystring.sort(req.url));
		call set_backend;
	}
}

sub vcl_hash {
	# Run this VCL only if the path is not to the polyfill-service v2 server
	if ((req.http.Orig-URL ~ "^/v3/") || req.http.Orig-URL ~ "^(?!/v2).+" && req.http.Orig-URL ~ "^(?!/__health).+" && req.http.Orig-URL ~ "^(?!/test/libs/proclaim/proclaim.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.css).+" && req.http.Orig-URL ~ "^(?!/__gtg).+") {
		if (req.http.Fastly-Debug) {
			call breadcrumb_hash;
		}

		# We are not adding req.http.host to the hash because we want https://cdn.polyfill.io and https://polyfill.io to be a single object in the cache.
		# set req.hash += req.http.host;
		set req.hash += req.url;
		# We include return(hash) to stop the function falling through to the default VCL built into varnish, which for vcl_hash will add req.url and req.http.Host to the hash.
		return(hash);
	}
}

sub vcl_miss {
	# Run this VCL only if the path is not to the polyfill-service v2 server
	if ((req.http.Orig-URL ~ "^/v3/") || req.http.Orig-URL ~ "^(?!/v2).+" && req.http.Orig-URL ~ "^(?!/__health).+" && req.http.Orig-URL ~ "^(?!/test/libs/proclaim/proclaim.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.css).+" && req.http.Orig-URL ~ "^(?!/__gtg).+") {
		if (req.http.Fastly-Debug) {
			call breadcrumb_miss;
		}
	}
}

sub vcl_pass {
	# Run this VCL only if the path is not to the polyfill-service v2 server
	if ((req.http.Orig-URL ~ "^/v3/") || req.http.Orig-URL ~ "^(?!/v2).+" && req.http.Orig-URL ~ "^(?!/__health).+" && req.http.Orig-URL ~ "^(?!/test/libs/proclaim/proclaim.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.css).+" && req.http.Orig-URL ~ "^(?!/__gtg).+") {
		if (req.http.Fastly-Debug) {
			call breadcrumb_pass;
		}
	}
}

sub vcl_fetch {
	# Run this VCL only if the path is not to the polyfill-service v2 server
	if ((req.http.Orig-URL ~ "^/v3/") || req.http.Orig-URL ~ "^(?!/v2).+" && req.http.Orig-URL ~ "^(?!/__health).+" && req.http.Orig-URL ~ "^(?!/test/libs/proclaim/proclaim.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.css).+" && req.http.Orig-URL ~ "^(?!/__gtg).+") {
		if (req.http.Fastly-Debug) {
			call breadcrumb_fetch;
		}

		# Ensure the site is only served over HTTPS and reduce the chances of someone performing a MITM attack.
		set beresp.http.Strict-Transport-Security = "max-age=31536000; includeSubdomains; preload";
		# Enables the cross-site scripting filter built into most modern web browsers.
		set beresp.http.X-XSS-Protection = "1; mode=block";
		# Prevents MIME-sniffing a response away from the declared content type.
		set beresp.http.X-Content-Type-Options = "nosniff";

		# Allow only content from the site's own origin (this excludes subdomains.)
		# Don't allow the website to be used within an iframe
		if (!beresp.http.Content-Security-Policy) {
			set beresp.http.Content-Security-Policy = "default-src 'self'; font-src 'self' https://www.ft.com; img-src 'self' https://www.ft.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'";
		}

		# The Referrer-Policy header governs which referrer information, sent in the Referer header, should be included with requests made.
		# Send a full URL when performing a same-origin request, but only send the origin of the document for other cases.
		set beresp.http.Referrer-Policy = "origin-when-cross-origin";

		# Enable purging of all objects in the Fastly cache by issuing a purge with the key "polyfill-service".
		if (beresp.http.Surrogate-Key !~ "\bpolyfill-service\b") {
            set beresp.http.Surrogate-Key = if(beresp.http.Surrogate-Key, beresp.http.Surrogate-Key " polyfill-service", "polyfill-service");
        }

		set beresp.http.Timing-Allow-Origin = "*";

		set beresp.http.Normalized-User-Agent = req.http.Normalized-User-Agent;
		set beresp.http.Detected-User-Agent = req.http.useragent_parser_family "/"  req.http.useragent_parser_major "." req.http.useragent_parser_minor "." req.http.useragent_parser_patch;
	}
}

sub vcl_deliver {
	# Run this VCL only if the path is not to the polyfill-service v2 server
	if ((req.http.Orig-URL ~ "^/v3/") || req.http.Orig-URL ~ "^(?!/v2).+" && req.http.Orig-URL ~ "^(?!/__health).+" && req.http.Orig-URL ~ "^(?!/test/libs/proclaim/proclaim.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.js).+" && req.http.Orig-URL ~ "^(?!/test/libs/mocha/mocha.css).+" && req.http.Orig-URL ~ "^(?!/__gtg).+") {
		if (req.http.Fastly-Debug) {
			call breadcrumb_deliver;
		}

		set req.http.Fastly-Force-Shield = "yes";

		# Allow cross origin GET, HEAD, and OPTIONS requests to be made.
		if (req.http.Origin) {
			set resp.http.Access-Control-Allow-Origin = "*";
			set resp.http.Access-Control-Allow-Methods = "GET,HEAD,OPTIONS";
		}

		add resp.http.Server-Timing = fastly_info.state {", fastly;desc="Edge time";dur="} time.elapsed.msec;

		if (req.http.Fastly-Debug) {
			set resp.http.Debug-Backend = req.http.Debug-Backend;
			set resp.http.Debug-Host = req.http.Host;
			set resp.http.Debug-Fastly-Restarts = req.restarts;
			set resp.http.Debug-URL = req.url;
			set resp.http.Debug-Orig-URL = req.http.Orig-URL;
			set resp.http.Debug-VCL-Route = req.http.X-VCL-Route;
			set resp.http.useragent_parser_family = req.http.useragent_parser_family;
			set resp.http.useragent_parser_major = req.http.useragent_parser_major;
			set resp.http.useragent_parser_minor = req.http.useragent_parser_minor;
			set resp.http.useragent_parser_patch = req.http.useragent_parser_patch;
		} else {
			unset resp.http.Server;
			unset resp.http.Via;
			unset resp.http.X-Cache;
			unset resp.http.X-Cache-Hits;
			unset resp.http.X-Served-By;
			unset resp.http.X-Timer;
			unset resp.http.Fastly-Restarts;
		}
	}
}

sub vcl_error {
	if (obj.status == 911) {
		set obj.status = 405;
		set obj.response = "METHOD NOT ALLOWED";
		set obj.http.Content-Type = "text/html; charset=utf-8";
		set obj.http.Cache-Control = "private, no-store";
		synthetic req.method " METHOD NOT ALLOWED";
		return (deliver);
	}
	if (obj.status == 912) {
		set obj.status = 200;
		set obj.response = "OK";
		set obj.http.Allow = "OPTIONS, GET, HEAD";
		return (deliver);
	}
}


Example VCL: Cleanup and update for varnish version >= 1.2

git-svn-id: 7d4b18ab7d176792635d6a7a77dd8cbbea8e8daa@2634 d4fa192b-c00b-0410-8231-f00ffab90ce4

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

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

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

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

sub vcl_recv {

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

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

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

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

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

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

        # The default vcl_recv is used from here.
 }

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

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

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

#
# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.
#
# $Id: default.vcl 1424 2007-05-15 19:38:56Z des $
#

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

backend default {
	set backend.host = "127.0.0.1";
	set backend.port = "9673";
}

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

sub vcl_recv {
	if (req.request != "GET" && req.request != "HEAD") {
        	# PURGE request if zope asks nicely
        	if (req.request == "PURGE") {
        	        if (!client.ip ~ purge) {
        	                error 405 "Not allowed.";
        	        }
        	        lookup;
        	}
		pipe;
	}
	if (req.http.Expect) {
		pipe;
	}
	if (req.http.Authenticate || req.http.Authorization) {
		pass;
	}
	# We only care about the "__ac.*" cookies, used for authentication
	if (req.http.Cookie && req.http.Cookie ~ "__ac(|_(name|password|persistent))=") {
		pass;
	}
	lookup;
}

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

# Enforce a minimum TTL, since we PURGE changed objects actively from Zope.
sub vcl_fetch {
        if (obj.ttl < 3600s) {
                set obj.ttl = 3600s;
        }
}

# Below is a commented-out copy of the default VCL logic.  If you
# redefine any of these subroutines, the built-in logic will be
# appended to your code.

## Called when a client request is received
#
#sub vcl_recv {
#	if (req.request != "GET" && req.request != "HEAD") {
#		pipe;
#	}
#	if (req.http.Expect) {
#		pipe;
#	}
#	if (req.http.Authenticate || req.http.Cookie) {
#		pass;
#	}
#	lookup;
#}
#
## Called when entering pipe mode
#
#sub vcl_pipe {
#	pipe;
#}
#
## Called when entering pass mode
#
#sub vcl_pass {
#	pass;
#}
#
## Called when entering an object into the cache
#
#sub vcl_hash {
#	hash;
#}
#
## Called when the requested object was found in the cache
#
#sub vcl_hit {
#	if (!obj.cacheable) {
#		pass;
#	}
#	deliver;
#}
#
## Called when the requested object was not found in the cache
#
#sub vcl_miss {
#	fetch;
#}
#
## Called when the requested object has been retrieved from the
## backend, or the request to the backend has failed
#
#sub vcl_fetch {
#	if (!obj.valid) {
#		error;
#	}
#	if (!obj.cacheable) {
#		pass;
#	}
#	if (resp.http.Set-Cookie) {
#		pass;
#	}
#	insert;
#}
#
## Called when an object nears its expiry time
#
#sub vcl_timeout {
#	discard;
#}


Added more vim commands

Added more vim commands

# Voice commands for gvim

include folders.vch;
include numbers.vch;
include letters.vch;
include pandas.vch;

Edit [My] Configuration = ':e $'  'MYVIMRC' {Enter};

<delimiters> := (Quotes = '"' | 'Single Quotes' = "'" | 'Double Quotes' = '"' | Parentheses = '('
		| Brackets = '[' | 'Curly Braces' = '{' | Braces = '{'
		| HTML = '<' | Tags = '<' | 'Close HTML' = '>' | 'Close Tag' = '>'	# NOTE: only works with commands I created
		| 'Equal Sign' = '='
);

# --- File Management ---------------
Open Document = {Alt+f} o;
List Files = {Esc}  ':bro ol' {Enter};
File <numbers> = $1  {Enter};
Latest File = {Esc}  ':bro ol' {Enter} 1{Enter};

Show (Projects | Folders) = {Esc}':e C:\Voice\folders.vch'  {Enter} Wait(100) {Down_9};
[Open] Project <folder> =  {Esc} ':e ' $1 {Enter} Wait(100) {Down_9};
[Open] Current Project = {Esc} ':e .' {Enter} Wait(100) {Down_9};
Up Directory = '-';
Search [Directory] <letters> = '/^' $1;
# :Sex = directory of current file
# :Vex for vertical split
#   :sp. = Split horizontally on current directory (:vsp. For vertical)
# % = new file

(List Buffers = 'ls' | Buffer List = 'ls' | 'Next Buffer' = 'bn' | 'Last Buffer' = 'bp' | 'Close Buffer' = 'bd'
	| 'Next Window' = 'bn' | 'Last Window' = 'bp' | 'Close Window' = 'bd')
	= {Esc}':' $1  {Enter};

Save Document = {Esc}  Wait(100) ':w' {Enter};
Save As = {Alt+f} a;
Go (Quit = 'quit' | Close = 'close' | 'Close without saving' = 'close!'  | Set = 'set') = ':' $1 {Enter};
[Go] Quit without Saving = {Esc}  Wait(100) ':quit!' {Enter};

Save and Reload = {Esc}  Wait(100) ':w' {Enter} Wait(100)  SendSystemKeys({Alt+Tab}) Wait(100) {Alt+r};

# --- Basic Navigation ---------------
<numbers> (Up = 'k' | Down = 'j' | Right = 'l' | Left = 'h') = $1 $2;
Homa = {Esc} 0;
Enda = {Esc}  '$';
(Top |  Go to Top) = {Esc} gg;
(Bottom | Go to Bottom) = {Esc} G;
(Move Forward | Move Right | Go Forward) <numbers> (words = 'w' |  blocks = '}') = $2 $3;
(Move Forward | Move Right | Go Forward) <numbers> (end | enda) words = $2 'e';
(Move Back | Move Left | Go Back) <numbers> (words = 'b' | blocks = '{') = $2 $3;
Fine (Text | Alpha) <letters> = {Esc} f $2;
Find Back <letters> = {Esc} F $1;
Fine Number 1..9 = {Esc} '/' $1 {Enter};
Go (Search | Find) = {Esc} '/\c';         # \c = case insensitive
Fine <delimiters> = {Esc} '/' $1 {Enter};
Insert (Before = 'i' | After = 'a') <delimiters> = {Esc} '/' $2 {Enter} Wait(100) $1;

(Find Again | Next Fine | Next Find) = n;
Line 1..100 = {Esc} ':' $1  {Enter};

# --- Basic Editing ---------------
(Do | Go) (Insert = 'i' | Append = 'A' |
Escape = '{Esc}') = $2;
(Insert = 'i' |
Escape = '{Esc}') = $1;
Nexta = {Esc} A {Enter};
Enda Insert = {Esc} A;

(Do Again | Repeat That) = '.';
(Undo | Undo That) = {Esc} u;
Redo = {Esc} {Ctrl+r};

(Kill = 'x') <numbers> = $2  $1;
(Kill | Delete) Word = 'dw';
(Kill | Delete) <numbers>  Words = 'd' $2 'w';
Dee Dee = 'dd';
Cut to End of Line = 'd$';
Cut to End of Word = 'de';

('Go Visual' | Mark | 'Visual Mode') = V;
('Delete That' = 'd' |  'Yank That' = 'y' | 'Paste' = 'p' | 'Change That' = 'c' | 'Join That' = 'J') = $1;
Paste That = {Esc} Wait(100) '"+gP';
Copy That = '"*y';
Copy Line = {Esc} V Wait(100)  '"*y';
Select Line = {Esc} V;

do test = {Esc} i  Wait(100) "strawberry";

(Delete= 'd' | Change = 'c') between <delimiters> = $1 i $2;
(Delete= 'd' | Change = 'c') (Next = '/' | Last = '?') <delimiters> = {Esc} $2 $3 {Enter} $1 i $3;
(Delete= 'd' | Change = 'c') (Next = '/' | Last = '?')
	(Heading = '<h' | Header = '<h' | Paragraph = '<p')
	= {Esc} $2 '\c' $3 {Enter} $1 i t;

Select All = {Alt+e}  s {Enter};
Grab Everything = {Alt+e} s  {Enter} Wait(100) {Alt+e} c {Enter} Wait(100) {Alt+Tab};

1..20 Spaces = Repeat($1, ' ');

Do Ultisnips = {Esc} ':UltiSnipsEdit'{Enter};

(Next = 'j' | Last = 'k') Tag = {Ctrl+$1};
Start Snippet= {Esc} i 'snippet  {Enter} {Enter}endsnippet' {Esc} {Up_2} A;
Start (
	D3 = 'd3_template' | Code = 'D3_code_wrapper'
| 	form | 'form text'  | 'form button' | 'form select'
) = {Esc} i $1  Wait(100)  {Tab};

Delete Line = 'dd';
Delete <numbers> Lines = $1 'dd';

# --- HTML and Bootstrap commands (until I'm sure I can get Ultisnips to work) ------------
Next One = {Esc} A;
Start (Heading | Header) 1..4 = '<h' $2 '></h' $2 '>'   Wait(100) {Left_5};
Start Div (id | class) = '
' Wait(100) {Left_2};
Stop Div = '
' {Enter};
Start Columns = {Esc} i  Wait(100) '
{Enter}
{Enter}'
		 {Enter} '
' {Enter}  '
'  {Esc} {Up_3}   Wait(100) A;
Start Column 1..12 = {Esc} a '
' {Enter} '
'{Enter}  {Esc} {Up_2} Wait(100) A;

# Paste Image = {Esc} a' ';
Paste Image = {Esc} a ' ';

# --- COMMANDS TO ADD ------------------------

# Using bookmarks or whatever they are called  e.g., if I accidentally do the wrong thing and end up on the wrong line,
# g;  - last place I was where I made a change
# Ctrl-o  - If I jump to a place, copy a line, I can snap back

# copying sets of lines, moving sets of lines
# :9yank - copy line 9
# :9t16 - copy line 9 to line 16
# :9.  - copy line 9 to current position

# For adding quotes, HTML tags, etc. to text that's already there:
# http://stackoverflow.com/questions/10305952/vim-enclose-word-in-tag

# Commands I'm not sure about
# H,M,L: top, middle, and bottom of screen

# Voice commands for gvim

include folders.vch;
include numbers.vch;
include letters.vch;
include pandas.vch;

Edit [My] Configuration = ':e $'  'MYVIMRC' {Enter};

<delimiters> := (Quotes = '"' | 'Single Quotes' = "'" | 'Double Quotes' = '"' | Parentheses = '('
		| Brackets = '[' | 'Curly Braces' = '{' | Braces = '{'
		| HTML = '<' | Tags = '<' | 'Close HTML' = '>' | 'Close Tag' = '>'	# NOTE: only works with commands I created
		| 'Equal Sign' = '='
);

# --- File Management ---------------
Open Document = {Alt+f} o;
List Files = {Esc}  ':bro ol' {Enter};
File <numbers> = $1  {Enter};
Latest File = {Esc}  ':bro ol' {Enter} 1{Enter};

Show (Projects | Folders) = {Esc}':e C:\Voice\folders.vch'  {Enter} Wait(100) {Down_9};
[Open] Project <folder> =  {Esc} ':e ' $1 {Enter} Wait(100) {Down_9};
[Open] Current Project = {Esc} ':e .' {Enter} Wait(100) {Down_9};
Up Directory = '-';
Search [Directory] <letters> = '/^' $1;
# :Sex = directory of current file
# :Vex for vertical split
#   :sp. = Split horizontally on current directory (:vsp. For vertical)
# % = new file

(List Buffers = 'ls' | Buffer List = 'ls' | 'Next Buffer' = 'bn' | 'Last Buffer' = 'bp' | 'Close Buffer' = 'bd'
	| 'Next Window' = 'bn' | 'Last Window' = 'bp' | 'Close Window' = 'bd')
	= {Esc}':' $1  {Enter};

Save Document = {Esc}  Wait(100) ':w' {Enter};
Save As = {Alt+f} a;
Go (Quit = 'quit' | Close = 'close' | 'Close without saving' = 'close!'  | Set = 'set') = ':' $1 {Enter};
[Go] Quit without Saving = {Esc}  Wait(100) ':quit!' {Enter};

# --- Basic Navigation ---------------
<numbers> (Up = 'k' | Down = 'j' | Right = 'l' | Left = 'h') = $1 $2;
Homa = {Esc} 0;
Enda = {Esc}  '$';
(Top |  Go to Top) = {Esc} gg;
(Bottom | Go to Bottom) = {Esc} G;
(Move Forward | Move Right | Go Forward) <numbers> (words = 'w' |  blocks = '}') = $2 $3;
(Move Forward | Move Right | Go Forward) <numbers> (end | enda) words = $2 'e';
(Move Back | Move Left | Go Back) <numbers> (words = 'b' | blocks = '{') = $2 $3;
Fine Text <letters> = {Esc} f $1;
Find Back <letters> = {Esc} F $1;
Fine Number 1..9 = {Esc} '/' $1 {Enter};
Fine <delimiters> = {Esc} '/' $1 {Enter};
Insert (Before = 'i' | After = 'a') <delimiters> = {Esc} '/' $2 {Enter} Wait(100) $1;

Next Fine = n;
Line 1..100 = {Esc} ':' $1  {Enter};

# --- Basic Editing ---------------
(Do | Go) (Insert = 'i' | Append = 'A' |
Escape = '{Esc}') = $2;
(Insert = 'i' |
Escape = '{Esc}') = $1;
Nexta = {Esc} A {Enter};

(Do Again | Repeat That) = '.';
(Undo | Undo That) = {Esc} u;
Redo = {Esc} {Ctrl+r};

(Kill = 'x') <numbers> = $2  $1;
(Kill | Delete) Word = 'dw';
(Kill | Delete) <numbers>  Words = 'd' $2 'w';
Dee Dee = 'dd';
Cut to End of Line = 'd$';
Cut to End of Word = 'de';

('Go Visual' | Mark | 'Visual Mode') = V;
('Delete That' = 'd' |  'Yank That' = 'y' | 'Paste That' = 'p' | 'Change That' = 'c') = $1;
Copy That = '"*y';
Copy Line = {Esc} V Wait(100)  '"*y';
Select Line = {Esc} V;

do test = {Esc} i  Wait(100) "strawberry";

(Delete= 'd' | Change = 'c') between <delimiters> = $1 i $2;
(Delete= 'd' | Change = 'c') (Next = '/' | Last = '?') <delimiters> = {Esc} $2 $3 {Enter} $1 i $3;
(Delete= 'd' | Change = 'c') (Next = '/' | Last = '?')
	(Heading = '<h' | Header = '<h' | Paragraph = '<p')
	= {Esc} $2 '\c' $3 {Enter} $1 i t;

Paste That = {Alt+e} p;
Select All = {Alt+e}  s {Enter};
Grab Everything = {Alt+e} s  {Enter} Wait(100) {Alt+e} c {Enter} Wait(100) {Alt+Tab};

1..20 Spaces = Repeat($1, ' ');

Do Ultisnips = {Esc} ':UltiSnipsEdit'{Enter};

(Next = 'j' | Last = 'k') Tag = {Ctrl+$1};
Start Snippet= {Esc} i 'snippet  {Enter} {Enter}endsnippet' {Esc} {Up_2} A;
Start (
	D3 = 'd3_template' | Code = 'D3_code_wrapper'
| 	form | 'form text'  | 'form button' | 'form select'
|	href
) = {Esc} i $1  Wait(100)  {Tab};

Delete Line = 'dd';
Delete <numbers> Lines = $1 'dd';

# --- HTML and Bootstrap commands (until I'm sure I can get Ultisnips to work) ------------
Next One = {Esc} A;
Start Div (id | class) = '
' Wait(100) {Left_2};
Stop Div = '
' {Enter};
Start Columns = {Esc} i  Wait(100) '
{Enter}
{Enter}'
		 {Enter} '
' {Enter}  '
'  {Esc} {Up_3}   Wait(100) A;
Start Column 1..12 = {Esc} a '
' {Enter} '
'{Enter}  {Esc} {Up_2} Wait(100) A;

# Paste Image = {Esc} a' ';
Paste Image = {Esc} a ' ';

###  NOTE: need to add the join command.
#    Try J

# ---Things to do
# change the configuration file

# Commands to add:
# g;  - last place I was where I made a change
# Ctrl-o  - If I jump to a place, copy a line, I can snap back
# :9yank - copy line 9
# :9t16 - copy line 9 to line 16
# :9.  - copy line 9 to current position

# Commands I'm not sure about
# H,M,L: top, middle, and bottom of screen


Unset cookie for images so varnish will cache them.

When extensions were added to the image urls this was not updated.

vcl 4.0;

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

sub vcl_recv {
  if (req.method == "GET" &&
      req.url ~ "^/((gend|src)_thumbs/\d+|(gend|src)_images/[\w-]{6,}\.(bmp|gif|jpg|png))$") {
      unset req.http.Cookie;
  }
}

vcl 4.0;

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

sub vcl_recv {
  if (req.method == "GET" &&
      req.url ~ "^/((gend|src)_thumbs/\d+|(gend|src)_images/[\w-]{6,})$") {
      unset req.http.Cookie;
  }
}


localhost might resolve to ipv4 and ipv6. using 127.0.0.1 instead.

backend default { .host = "127.0.0.1"; }

backend default { .host = "localhost"; }


vcl 4.0 marker

vcl 4.0 marker

vcl 4.0; backend default { .host = "localhost"; }


Do not stop vcl_recv logic

Do not stop vcl_recv logic

That return statement has nasty consequences: the rest of the caching
logic is skipped, so no cache lookup is done!

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
}

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


Update main.vcl

Update main.vcl

import boltsort;

sub vcl_recv {
#FASTLY recv

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return(lookup);
}

sub vcl_deliver {
#FASTLY deliver

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

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

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

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

	set resp.http.Age = "0";

	return(deliver);
}

sub vcl_error {
#FASTLY error

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

import boltsort;

sub vcl_recv {
#FASTLY recv

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return(lookup);
}

sub vcl_deliver {
#FASTLY deliver

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

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

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

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

	set resp.http.Age = 0;

	return(deliver);
}

sub vcl_error {
#FASTLY error

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


Adds CORS headers to varnish responses

Adds CORS headers to varnish responses

vcl 4.0;

backend default {
    .host = "${VARNISH_BACKEND_HOST}";
    .port = "${VARNISH_BACKEND_PORT}";
}

sub vcl_recv {

    if (req.method == "POST" && !req.http.Content-Length) {
        return (synth(411, "Content-Length required"));
    }

    # Require that the content be less than 8000 characters
    if (req.method == "POST" && !req.http.Content-Length ~ "^[1-7]?[0-9]{1,3}$") {
        return (synth(413, "Request content too large (>8000)"));
    }

}

sub vcl_backend_response {
    if (bereq.method == "GET") {
        set beresp.ttl = 60m;
    }

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

    if (bereq.method == "OPTIONS") {
        set beresp.http.Access-Control-Allow-Origin = "*";
        set beresp.http.Access-Control-Allow-Methods = "GET, POST, OPTIONS";
        set beresp.http.Access-Control-Allow-Headers = "Origin, Accept, Content-Type, X-Requested-With";
    }

    set beresp.do_gzip = true;
}

vcl 4.0;

backend default {
    .host = "${VARNISH_BACKEND_HOST}";
    .port = "${VARNISH_BACKEND_PORT}";
}

sub vcl_recv {

    if (req.method == "POST" && !req.http.Content-Length) {
        return (synth(411, "Content-Length required"));
    }

    # Require that the content be less than 8000 characters
    if (req.method == "POST" && !req.http.Content-Length ~ "^[1-7]?[0-9]{1,3}$") {
        return (synth(413, "Request content too large (>8000)"));
    }

}

sub vcl_backend_response {
    if (bereq.method == "GET") {
        set beresp.ttl = 60m;
    }

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

    set beresp.do_gzip = true;
}


removing server header from default.vcl

removing server header from default.vcl

director app dns {
	{
		.backend = {
			.host = "%(app_host)s";
			.port = "80";
		}
	}
	.ttl = 5m;

}

sub vcl_recv {
	set req.http.X-Host = req.http.host;
	set req.http.Host = "%(app_host)s";

	if(req.url ~ "/_varnish_healthcheck") {
		error 200 "WORKING";
		set req.http.Connection = "close";
	}
}

sub vcl_fetch {
 	unset beresp.http.Server;
	if(beresp.http.X-Esi) {
		set beresp.do_esi = true;
		unset beresp.http.X-Esi;
	}
}

director app dns {
	{
		.backend = {
			.host = "%(app_host)s";
			.port = "80";
		}
	}
	.ttl = 5m;

}

sub vcl_recv {
	set req.http.X-Host = req.http.host;
	set req.http.Host = "%(app_host)s";

	if(req.url ~ "/_varnish_healthcheck") {
		error 200 "WORKING";
		set req.http.Connection = "close";
	}
}

sub vcl_fetch {
	if(beresp.http.X-Esi) {
		set beresp.do_esi = true;
		unset beresp.http.X-Esi;
	}
}


keyword idでひっかけるので全体一致が正しい

keyword idでひっかけるので全体一致が正しい

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

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

import directors;

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

sub vcl_init {
    new backend = directors.round_robin();

    backend.add_backend(localhost);
}

sub vcl_recv {
  set req.backend_hint = backend.backend();

  if (req.method == "INVALIDATE") {
     ban("obj.http.Keyword = ^.*" + req.http.X-Invalidated-Keyword + ".*$");
    return(synth(200, req.http.X-Invalidated-Keyword + " Purged. Success !!"));
  }

  # if (req.http.Cookie) {
  #     return(pass);
  # }

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

  return(hash);
}

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

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

    return(deliver);
}

sub vcl_backend_response {

  set beresp.do_esi = true;
  # this is same as builtin vcl_backend_response
  # https://www.varnish-cache.org/trac/browser/bin/varnishd/builtin.vcl?rev=ae548683b8f91d0a92799f6c746b80773a4c9f05
  if (beresp.ttl <= 0s ||
    beresp.http.Set-Cookie ||
    beresp.http.Surrogate-control ~ "no-store" ||
    beresp.http.Cache-Control ~ "no-cache" ||
    (!beresp.http.Surrogate-Control &&
      beresp.http.Cache-Control ~ "no-cache|no-store|private") ||
    beresp.http.Vary == "*") {
      /*
      * Mark as "Hit-For-Pass" for the next 2 minutes
      */
      set beresp.uncacheable = true;
  }

  # this is hatena original config
  if (beresp.ttl > 0s && ! beresp.uncacheable) {
      set beresp.grace = 1m;
  }
  return (deliver);
}

sub vcl_synth {
    set resp.http.Content-Type = "text/html; charset=utf-8";
    set resp.http.Retry-After = "5";
    synthetic( {"<!DOCTYPE html>

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

"} + resp.status + " " + resp.reason + {"

"} + resp.reason + {"

XID: "} + req.xid + {"

"} );
    return (deliver);
}

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

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

import directors;

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

sub vcl_init {
    new backend = directors.round_robin();

    backend.add_backend(localhost);
}

sub vcl_recv {
  set req.backend_hint = backend.backend();

  if (req.method == "INVALIDATE") {
     ban("obj.http.Keyword ~ ^.*" + req.http.X-Invalidated-Keyword + ".*$");
    return(synth(200, req.http.X-Invalidated-Keyword + " Purged. Success !!"));
  }

  # if (req.http.Cookie) {
  #     return(pass);
  # }

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

  return(hash);
}

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

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

    return(deliver);
}

sub vcl_backend_response {

  set beresp.do_esi = true;
  # this is same as builtin vcl_backend_response
  # https://www.varnish-cache.org/trac/browser/bin/varnishd/builtin.vcl?rev=ae548683b8f91d0a92799f6c746b80773a4c9f05
  if (beresp.ttl <= 0s ||
    beresp.http.Set-Cookie ||
    beresp.http.Surrogate-control ~ "no-store" ||
    beresp.http.Cache-Control ~ "no-cache" ||
    (!beresp.http.Surrogate-Control &&
      beresp.http.Cache-Control ~ "no-cache|no-store|private") ||
    beresp.http.Vary == "*") {
      /*
      * Mark as "Hit-For-Pass" for the next 2 minutes
      */
      set beresp.uncacheable = true;
  }

  # this is hatena original config
  if (beresp.ttl > 0s && ! beresp.uncacheable) {
      set beresp.grace = 1m;
  }
  return (deliver);
}

sub vcl_synth {
    set resp.http.Content-Type = "text/html; charset=utf-8";
    set resp.http.Retry-After = "5";
    synthetic( {"<!DOCTYPE html>

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

"} + resp.status + " " + resp.reason + {"

"} + resp.reason + {"

XID: "} + req.xid + {"

"} );
    return (deliver);
}


Added set cookie in example VCL

Added set cookie in example VCL

import std;
import abtest;

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

/* Only permit localhost to manipulate abtest configuration */
acl abconfig {
    "localhost";
}

sub vcl_init {
    if (abtest.load_config("/tmp/abtest.cfg") != 0) {
        C{ syslog(LOG_ALERT, "Unable to load AB config from /tmp/abtest.cfg"); }C
    }
    abtest.set_rule(".*", "a:1;b:1;86400;"); // Set default rule with a duration of 24H

    return (ok);
}

sub vcl_fini {
}

sub vcl_recv {
    if (req.http.X-AB-Cfg) {
        if (!client.ip ~ abconfig) {
            error 405 "Not allowed.";
        } else {
            // curl localhost:8080 -X PUT -H "X-AB-Cfg:base" -H "X-AB-Cfg-Val:a:25;b:75;"
            if (req.request == "PUT") {
                std.log("AB Config PUT request: " + req.http.X-AB-Cfg + "|" + req.http.X-AB-Cfg-Val);
                abtest.set_rule(req.http.X-AB-Cfg, req.http.X-AB-Cfg-Val);
                if (abtest.save_config("/tmp/abtest.cfg") != 0) {
                    std.log("ABTest - Error, could not save the configuration");
                }
            }

            // curl localhost:8080 -X DELETE -H "X-AB-Cfg:base"
            if (req.request == "DELETE") {
                std.log("AB Config DELETE request: " + req.http.X-AB-Cfg);
                abtest.rem_rule(req.http.X-AB-Cfg);
            }
        }
    }

    if(!req.http.Cookie ~ "abtesting") {
        std.log("No AB cookie, setting it.");
        set req.http.Cookie = "abtesting=" + abtest.get_rand(req.url) + "; path=/; expires=" + abtest.get_expire(req.url);
        set req.http.X-Set-AB-Cookie = req.http.Cookie;
    } else {
        std.log("AB cookie found.");
    }

    return (lookup);
}

sub vcl_pipe {
}

sub vcl_pass {
}

sub vcl_hash {
}

sub vcl_hit {
}

sub vcl_miss {
}

sub vcl_fetch {
}

sub vcl_deliver {
    if (req.http.X-Set-AB-Cookie) {
        std.log("Setting cookie to " + req.http.X-Set-AB-Cookie);
        set resp.http.Set-Cookie = req.http.X-Set-AB-Cookie;
    }

    if (req.http.X-AB-Cfg && client.ip ~ abconfig) {
        if (req.request == "GET") {
            // curl localhost:8080 -X GET -H "X-AB-Cfg:;"
            set resp.http.X-AB-Cfg = abtest.get_rules();
        }
    }
}

sub vcl_error {
}

 import std;
 import abtest;

 C{
    #include <syslog.h>
 }C

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

/* Only permit localhost to manipulate abtest configuration */
acl abconfig {
    "localhost";
}

sub vcl_init {
    if (abtest.load_config("/tmp/abtest.cfg") != 0) {
        C{ syslog(LOG_ALERT, "Unable to load AB config from /tmp/abtest.cfg"); }C
    }

    return (ok);
}

sub vcl_fini {
}

sub vcl_recv {
    std.log(abtest.get_rules());

    if (req.http.X-AB-Cfg) {
        if (!client.ip ~ abconfig) {
            std.log("AB Config request not allowed from " + client.ip);
            error 405 "Not allowed.";
        } else {
            // curl localhost:8080 -X PUT -H "X-AB-Cfg:base" -H "X-AB-Cfg-Val:a:25;b:75;"
            if (req.request == "PUT") {
                std.log("AB Config PUT request: " + req.http.X-AB-Cfg + "|" + req.http.X-AB-Cfg-Val);
                abtest.set_rule(req.http.X-AB-Cfg, req.http.X-AB-Cfg-Val);
                if (abtest.save_config("/tmp/abtest.cfg") != 0) {
                    std.log("ABTest - Error, could not save the configuration");
                }
            }

            // curl localhost:8080 -X DELETE -H "X-AB-Cfg:base"
            if (req.request == "DELETE") {
                std.log("AB Config DELETE request: " + req.http.X-AB-Cfg);
                abtest.rem_rule(req.http.X-AB-Cfg);
            }

            // curl localhost:8080 -X GET -H "X-AB-Cfg;"
            if (req.request == "GET") {
                std.log("AB Config GET request: " + req.http.X-AB-Cfg);
                std.log("CFG -> " + abtest.get_rules());
            }
        }
    }

    std.log("AB Cookie for '" + req.url + "': " + abtest.get_rand(req.url) + ", cookie should last for " + abtest.get_duration(req.url));

/*
    if(req.http.Cookie ~ "abtesting") {
    }
*/
}

sub vcl_pipe {
}

sub vcl_pass {
}

sub vcl_hash {
}

sub vcl_hit {
}

sub vcl_miss {
}

sub vcl_fetch {
}

sub vcl_deliver {
}

sub vcl_error {
}