Restore the warehouse(-staging).python.org redirects

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, "#.*$", "");

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

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

    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, "#.*$", "");

    # 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 www.pypi.io to pypi.io, this is purposely done *after* the HTTPS
    # checks.
    if (std.tolower(req.http.host) == "www.pypi.io") {
        set req.http.Location = "https://pypi.io" 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);
        }
    }

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

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

}


Fix 400 bad request of varnish with nginx and apache 2.4

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

(ActionItem3186)

C{

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

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

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

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

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

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

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

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

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

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

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

    return is_supported;
}

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

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

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

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

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

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

        q = 1.0;

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

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

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

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

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

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

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

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

    RETURN_DEFAULT_LANG;
}

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

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

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

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

    return;
}

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

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

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

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

C{

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

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

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

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

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

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

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

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

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

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

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

    return is_supported;
}

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

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

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

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

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

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

        q = 1.0;

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

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

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

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

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

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

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

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

    RETURN_DEFAULT_LANG;
}

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

    memset(lang, 0, LANG_MAXLEN);

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

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

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

    return;
}

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

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

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

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


varnish fix uncachable status having no ttl

vcl 4.0;

# Disable esi xml check on varnish Startup
# Example:
#   DAEMON_OPTS="-a :6081 \
#     -T localhost:6082 \
#     -p feature=+esi_disable_xml_check
#     -p feature=+esi_ignore_https
#

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

sub vcl_recv {
        if (req.method != "GET") {
                return (pass);
        }
        if (req.http.Cookie ~ "\bvarnish-force=pass") {
                return (pass);
        }

        return (hash);
}

sub vcl_hash {
        hash_data(req.url);
        return(lookup);
}

sub vcl_backend_response {
        if (beresp.http.Cache-Control != "public") {
                set beresp.uncacheable = true;
                set beresp.ttl = 3m;
                return (deliver);
        }

        set beresp.do_esi = true;
        set beresp.ttl = 15m;

        unset beresp.http.Set-Cookie;
}

vcl 4.0;

# Disable esi xml check on varnish Startup
# Example:
#   DAEMON_OPTS="-a :6081 \
#     -T localhost:6082 \
#     -p feature=+esi_disable_xml_check
#     -p feature=+esi_ignore_https
#

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

sub vcl_recv {
        if (req.method != "GET") {
                return (pass);
        }
        if (req.http.Cookie ~ "\bvarnish-force=pass") {
                return (pass);
        }

        return (hash);
}

sub vcl_hash {
        hash_data(req.url);
        return(lookup);
}

sub vcl_backend_response {
        if (beresp.http.Cache-Control != "public") {
                set beresp.uncacheable = true;
                return (deliver);
        }

        set beresp.do_esi = true;
        set beresp.ttl = 15m;

        unset beresp.http.Set-Cookie;
}


Varies fixes

Varies fixes

#
# Varnish VCL for backend cacheable
#
# Author Karto Martin <source@karto.net>
# Copyright (c) 2015 Karto Martin. All Right Reserved.
# License The MIT License
#

sub cacheable_backend_response_cacheable {

    ###
    # rfc7231   Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
    # rfc7234   Hypertext Transfer Protocol (HTTP/1.1): Caching
    # rfc7234   3.  Storing Responses in Caches    https://tools.ietf.org/html/rfc7234#section-3
    #
    # A cache MUST NOT store a response to any request, unless:

    # o  The request method is understood by the cache and defined as being
    #    cacheable, and
    # rfc7231   4.2.3.  Cacheable Methods    https://tools.ietf.org/html/rfc7231#section-4.2.3
    if ("GET" != bereq.method && "HEAD" != bereq.method) {
        set beresp.http.X-Cacheable = "NO;method";
        set beresp.uncacheable = true;
        set beresp.ttl = 0s; // No hit for pass
    }

    ## varnish - default understood status codes
    ## : 200 203         300 301 302 304 307 404     410
    ## rfc7231 - cacheable by default status codes
    ## : 200 203 204 206 300 301             404 405 410 414 501
    ## consider for default : 204 205
    ## consider for freshness: 302 303 307 401 403 405 406 500 501 502 503 504 505
    ## never be cached: 201 202 305 306 402 408 409 411 412 413 415 417
    #
    ###: custom understood status codes
    ## : 200 203 204 205 300 301 302 303 304 307 308
    ## : 400 403 404 405 406 410 414 418
    ## : 500 501 502 503 504 505

    # o  the response status code is understood by the cache, and
    # rfc7231   6.1.  Overview of Status Codes    https://tools.ietf.org/html/rfc7231#section-6.1
    elsif (regsub(beresp.status, "", "") !~ "^(20[0345]|30[0123478]|40[03456]|41[048]|50[012345])$") {
        set beresp.http.X-Cacheable = "H4P;status";
        set beresp.uncacheable = true;
        set beresp.ttl = 119s; // Hit for pass
    }

    # o  the "no-store" cache directive (see Section 5.2) does not appear
    #    in request or response header fields, and
    # rfc7234   5.2.1.5.  no-store    https://tools.ietf.org/html/rfc7234#section-5.2.1.5
    elsif (bereq.http.Cache-Control ~ "(?i)(^|,)\s*no-store\s*(,|$)") {
        set beresp.http.X-Cacheable = "NO;req;no-store";
        set beresp.uncacheable = true;
        set beresp.ttl = 0s; // No hit for pass
    }
    # rfc7234   5.2.2.3.  no-store    https://tools.ietf.org/html/rfc7234#section-5.2.2.3
    elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*no-store\s*(,|$)") {
        set beresp.http.X-Cacheable = "H4P;resp;no-store";
        set beresp.uncacheable = true;
        set beresp.ttl = 119s; // Hit for pass
    }

    # o  the "private" response directive (see Section 5.2.2.6) does not
    #    appear in the response, if the cache is shared, and
    # rfc7234   5.2.2.6.  private    https://tools.ietf.org/html/rfc7234#section-5.2.2.6
    elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*private\s*(,|$)") {
        set beresp.http.X-Cacheable = "H4P;private";
        set beresp.uncacheable = true;
        set beresp.ttl = 119s; // Hit for pass
    }

    # o  the Authorization header field (see Section 4.2 of [RFC7235]) does
    #    not appear in the request, if the cache is shared, unless the
    #    response explicitly allows it (see Section 3.2), and
    # rfc7234   3.2.  Storing Responses to Authenticated Requests    https://tools.ietf.org/html/rfc7234#section-3.2
    elsif (bereq.http.Authorization && beresp.http.Cache-Control !~ "(?i)(^|,)\s*(must-revalidate|public|s-maxage=\d+|dr-maxage=\d+)\s*(?=,|$)") {
        set beresp.http.X-Cacheable = "NO:authorization";
        set beresp.uncacheable = true;
        set beresp.ttl = 0s; // No hit for pass
    }

    # o  the response either:
    # rfc7234   4.2.1.  Calculating Freshness Lifetime  https://tools.ietf.org/html/rfc7234#section-4.2.1
    # A cache can calculate the freshness lifetime (denoted as
    # freshness_lifetime) of a response by using the first match of the
    # following:

    #    o  If the cache is varnish and the dr-maxage response directive
    #       is present, use its value, or
    #    *  contains a dr-maxage=ttl[+grace] response directive (custom varnish ttl and grace)
    elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*dr-maxage=\d+(\+\d+)?\s*(,|$)") {
        set beresp.http.X-Cacheable = "YES;"+regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*(dr-maxage=\d+(?:\+\d+)?)\s*(?:,.*)?$", "\1");
        set beresp.ttl = std.duration(regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*dr-maxage=(\d+)(?:\+\d+)?\s*(?:,.*)?$", "\1")+"s", 0s);
        if (beresp.http.Cache-Control ~ "(?i)(^|,)\s*dr-maxage=\d+\+\d+\s*(,|$)") {
            set beresp.grace = std.duration(regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*dr-maxage=\d+\+(\d+)\s*(?:,.*)?$", "\1")+"s", 0s);
        }
    }

    #    o  If the cache is shared and the s-maxage response directive
    #       (Section 5.2.2.9) is present, use its value, or
    #    *  contains a s-maxage response directive (see Section 5.2.2.9)
    #       and the cache is shared, or
    # rfc7234   5.2.2.9.  s-maxage    https://tools.ietf.org/html/rfc7234#section-5.2.2.9
    elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*s-maxage=\d+\s*(,|$)") {
        set beresp.http.X-Cacheable = "YES;"+regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*(s-maxage=\d+)\s*(?:,.*)?$", "\1");
        set beresp.ttl = std.duration(regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*s-maxage=(\d+)\s*(?:,.*)?$", "\1")+"s", 0s);
    }

    #    o  If the max-age response directive (Section 5.2.2.8) is present,
    #       use its value, or
    #    *  contains a max-age response directive (see Section 5.2.2.8), or
    # rfc7234   5.2.2.8.  max-age    https://tools.ietf.org/html/rfc7234#section-5.2.2.8
    elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*max-age=\d+\s*(,|$)") {
        set beresp.http.X-Cacheable = "YES;"+regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*(max-age=\d+)\s*(?:,.*)?$", "\1");
        set beresp.ttl = std.duration(regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*max-age=(\d+)\s*(?:,.*)?$", "\1")+"s", 0s);
    }

    #    o  If the Expires response header field (Section 5.3) is present, use
    #       its value minus the value of the Date response header field, or
    #    *  contains an Expires header field (see Section 5.3), or
    # rfc7234   5.3.  Expires    https://tools.ietf.org/html/rfc7234#section-5.3
    elsif (beresp.http.Expires) {
        if (std.time(beresp.http.Expires, now) > std.time(beresp.http.Date, now)) {
            set beresp.http.X-Cacheable = "YES;"+"expires="+(std.time(beresp.http.Expires, now) - std.time(beresp.http.Date, now));
            set beresp.ttl = std.time(beresp.http.Expires, now) - std.time(beresp.http.Date, now);
        }
        else {
            set beresp.http.X-Cacheable = "H4P;"+"expires=past";
            set beresp.uncacheable = true;
            set beresp.ttl =  119s; // Hit for pass
        }
    }

    #    o  Otherwise, no explicit expiration time is present in the response.
    #       A heuristic freshness lifetime might be applicable; see
    #       Section 4.2.2.

    #    *  contains a Cache Control Extension (see Section 5.2.3) that
    #       allows it to be cached, or
    #elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*extension\s*(,|$)") { }

    ## varnish - default understood status codes
    ## : 200 203         300 301 302 304 307 404     410
    ## rfc7231 - cacheable by default status codes
    ## : 200 203 204 206 300 301             404 405 410 414 501
    ## consider for default : 204 205
    ## consider for freshness: 302 303 307 401 403 405 406 500 501 502 503 504 505
    ## never be cached: 201 202 305 306 402 408 409 411 412 413 415 417
    ##
    ###: custom understood status codes
    ## : 200 203 204 205 206 300 301 302 303 304 307 308
    ## : 400 403 404 405 406 410 414 418
    ## : 500 501 502 503 504 505
    ##
    ###: custom cacheable by default
    ## : 200 203 204 300 301 302 303 304 307 308
    ## : 404 405 410 414 418 500 501 502 503 504

    #    *  has a status code that is defined as cacheable by default (see
    #       Section 4.2.2), or
    # rfc7231   6.1.  Overview of Status Codes    https://tools.ietf.org/html/rfc7231#section-6.1
    # rfc7231 and varnish default status codes : 200 203 204 300 301 302 304 307 404 405 410 414 501
    elsif (regsub(beresp.status, "", "") ~ "^(20[034]|30[01247]|40[45]|41[04]|501)$") {
        if (beresp.http.Last-Modified && std.time(beresp.http.Last-Modified, now) < now) {
            set beresp.http.X-Cacheable = "YES:default=10s<"+regsub((now - std.time(beresp.http.Last-Modified, now)) * 0.1, "\.\d\d\d$", "")+"s<1d";
            if ((now - std.time(beresp.http.Last-Modified, now)) * 0.1 <= 10s) {
                set beresp.ttl = 10s;
            }
            elsif ((now - std.time(beresp.http.Last-Modified, now)) * 0.1 < 1d) {
                set beresp.ttl = (now - std.time(beresp.http.Last-Modified, now)) * 0.1;
            }
            else {
                set beresp.ttl = 1d;
            }
        }
        else {
            set beresp.http.X-Cacheable = "YES:default=119s";
            set beresp.ttl = 119s;
        }
    }
    ## Extended default status codes
    ## : 303 308 418 500 502 503 504
    elsif (regsub(beresp.status, "", "") ~ "^(30[38]|418|50[0234])$") {
        if (beresp.http.Last-Modified && std.time(beresp.http.Last-Modified, now) < now) {
            set beresp.http.X-Cacheable = "YES:extended=10s<"+regsub((now - std.time(beresp.http.Last-Modified, now)) * 0.01, "\.\d\d\d$", "")+"<1h";
            if ((now - std.time(beresp.http.Last-Modified, now)) * 0.01 <= 10s) {
                set beresp.ttl = 10s;
            }
            elsif ((now - std.time(beresp.http.Last-Modified, now)) * 0.01 < 1h) {
                set beresp.ttl = (now - std.time(beresp.http.Last-Modified, now)) * 0.01;
            }
            else {
                set beresp.ttl = 1h;
            }
        }
        else {
            set beresp.http.X-Cacheable = "YES:extended=19s";
            set beresp.ttl = 19s;
        }
    }

    #    *  contains a public response directive (see Section 5.2.2.5).
    # rfc7234   5.2.2.5.  public    https://tools.ietf.org/html/rfc7234#section-5.2.2.5
    elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*public\s*(,|$)") {
        if (beresp.http.Last-Modified && std.time(beresp.http.Last-Modified, now) < now) {
            set beresp.http.X-Cacheable = "YES:public=10s<"+regsub((now - std.time(beresp.http.Last-Modified, now)) * 0.1, "\.\d\d\d$", "")+"<1d";
            if ((now - std.time(beresp.http.Last-Modified, now)) * 0.1 <= 10s) {
                set beresp.ttl = 10s;
            }
            elsif ((now - std.time(beresp.http.Last-Modified, now)) * 0.1 < 1d) {
                set beresp.ttl = (now - std.time(beresp.http.Last-Modified, now)) * 0.1;
            }
            else {
                set beresp.ttl = 1d;
            }
        }
        else {
            set beresp.http.X-Cacheable = "YES:public=119s";
            set beresp.ttl = 119s;
        }
    }

    # The rest - no matching rules
    else {
        set beresp.http.X-Cacheable = "NO:others";
        set beresp.uncacheable = true;
        set beresp.ttl = 0s; // No hit for pass
    }
    #End elseif

    ####################################################
    # Set grace
    if (beresp.http.Cache-Control !~ "(?i)(^|,)\s*dr-maxage=\d+\+\d+\s*(,|$)") {

        # 3.  The stale-while-revalidate Cache-Control Extension    http://tools.ietf.org/html/rfc5861#section-3
        #   When present in an HTTP response, the stale-while-revalidate Cache-
        #   Control extension indicates that caches MAY serve the response in
        #   which it appears after it becomes stale, up to the indicated number
        #   of seconds.
        if (beresp.http.Cache-Control ~ "(?i)(^|,)\s*stale-while-revalidate=\d*\s*(,|$)") {
            set beresp.http.VAR-stale-while-revalidate = std.duration(regsub(
                    beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*stale-while-revalidate=(\d+)\s*(?:,.*)?$", "\1s"), 0s)+"s";
        }

        # 4.  The stale-if-error Cache-Control Extension    http://tools.ietf.org/html/rfc5861#section-4
        #   The stale-if-error Cache-Control extension indicates that when an
        #   error is encountered, a cached stale response MAY be used to satisfy
        #   the request, regardless of other freshness information.
        if (beresp.http.Cache-Control ~ "(?i)(^|,)\s*stale-if-error=\d+\s*(,|$)") {
            set beresp.http.VAR-stale-if-error = std.duration(regsub(
                    beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*stale-if-error=(\d+)\s*(?:,.*)?$", "\1s"), 0s)+"s";
        }

        # Default grace
        if (beresp.http.Last-Modified && std.time(beresp.http.Last-Modified, now) < now) {
            if ((now - std.time(beresp.http.Last-Modified, now)) * 0.5 <= 10s) {
                set beresp.http.VAR-stale-default = "10s";
            }
            elsif ((now - std.time(beresp.http.Last-Modified, now)) * 0.5 < 1d) {
                set beresp.http.VAR-stale-default = ((now - std.time(beresp.http.Last-Modified, now)) * 0.5)+"s";
            }
            else {
                set beresp.http.VAR-stale-default = "86400s";
            }
        }
        else {
            set beresp.http.VAR-stale-default = "59s";
        }

        # Pick largest grace
        if (std.duration(beresp.http.VAR-stale-while-revalidate, 0s) >= std.duration(beresp.http.VAR-stale-if-error, 0s) &&
                std.duration(beresp.http.VAR-stale-while-revalidate, 0s) > std.duration(beresp.http.VAR-stale-default, 0s)) {
            set beresp.http.X-Cacheable = beresp.http.X-Cacheable+";swr;grace="+std.duration(beresp.http.VAR-stale-while-revalidate, 0s);
            set beresp.grace = std.duration(beresp.http.VAR-stale-while-revalidate, 0s);
        }
        elsif (std.duration(beresp.http.VAR-stale-if-error, 0s) >= std.duration(beresp.http.VAR-stale-while-revalidate, 0s) &&
                std.duration(beresp.http.VAR-stale-if-error, 0s) > std.duration(beresp.http.VAR-stale-default, 0s)) {
            set beresp.http.X-Cacheable = beresp.http.X-Cacheable+";sie;grace="+std.duration(beresp.http.VAR-stale-if-error, 0s);
            set beresp.grace = std.duration(beresp.http.VAR-stale-if-error, 0s);
        }
        else {
            set beresp.http.X-Cacheable = beresp.http.X-Cacheable+";def;grace="+std.duration(beresp.http.VAR-stale-default, 0s);
            set beresp.grace = std.duration(beresp.http.VAR-stale-default, 0s);
        }

    }

    # Set cache info for verbose
    set beresp.http.X-Caching = beresp.ttl + "/" + beresp.grace + "/" + beresp.keep + "/" + beresp.uncacheable;

    # cacheable for freshness and stale serving
    if ( ! beresp.uncacheable) {
        set beresp.http.VAR-freshness_lifetime = beresp.ttl+"s";
        if (beresp.ttl == 0s) {
            std.log("cacheable_backend_response_cacheable: ttl=1ms hack for stale serving");
            set beresp.ttl = 1ms;
        }
    }

}

#
# Varnish VCL for backend cacheable
#
# Author Karto Martin <source@karto.net>
# Copyright (c) 2015 Karto Martin. All Right Reserved.
# License The MIT License
#

sub cacheable_backend_response_cacheable {

    ###
    # rfc7231   Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
    # rfc7234   Hypertext Transfer Protocol (HTTP/1.1): Caching
    # rfc7234   3.  Storing Responses in Caches    https://tools.ietf.org/html/rfc7234#section-3
    #
    # A cache MUST NOT store a response to any request, unless:

    # o  The request method is understood by the cache and defined as being
    #    cacheable, and
    # rfc7231   4.2.3.  Cacheable Methods    https://tools.ietf.org/html/rfc7231#section-4.2.3
    if ("GET" != bereq.method && "HEAD" != bereq.method) {
        set beresp.http.X-Cacheable = "NO;method";
        set beresp.uncacheable = true;
        set beresp.ttl = 0s; // No hit for pass
    }

    ## varnish - default understood status codes
    ## : 200 203         300 301 302 304 307 404     410
    ## rfc7231 - cacheable by default status codes
    ## : 200 203 204 206 300 301             404 405 410 414 501
    ## consider for default : 204 205
    ## consider for freshness: 302 303 307 401 403 405 406 500 501 502 503 504 505
    ## never be cached: 201 202 305 306 402 408 409 411 412 413 415 417
    #
    ###: custom understood status codes
    ## : 200 203 204 205 300 301 302 303 304 307 308
    ## : 400 403 404 405 406 410 414 418
    ## : 500 501 502 503 504 505

    # o  the response status code is understood by the cache, and
    # rfc7231   6.1.  Overview of Status Codes    https://tools.ietf.org/html/rfc7231#section-6.1
    elsif (regsub(beresp.status, "", "") !~ "^(20[0345]|30[0123478]|40[03456]|41[048]|50[012345])$") {
        set beresp.http.X-Cacheable = "H4P;status";
        set beresp.uncacheable = true;
        set beresp.ttl = 119s; // Hit for pass
    }

    # o  the "no-store" cache directive (see Section 5.2) does not appear
    #    in request or response header fields, and
    # rfc7234   5.2.1.5.  no-store    https://tools.ietf.org/html/rfc7234#section-5.2.1.5
    elsif (bereq.http.Cache-Control ~ "(?i)(^|,)\s*no-store\s*(,|$)") {
        set beresp.http.X-Cacheable = "H4P;req;no-store";
        set beresp.uncacheable = true;
        set beresp.ttl = 119s; // Hit for pass
    }
    # rfc7234   5.2.2.3.  no-store    https://tools.ietf.org/html/rfc7234#section-5.2.2.3
    elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*no-store\s*(,|$)") {
        set beresp.http.X-Cacheable = "H4P;resp;no-store";
        set beresp.uncacheable = true;
        set beresp.ttl = 119s; // Hit for pass
    }

    # o  the "private" response directive (see Section 5.2.2.6) does not
    #    appear in the response, if the cache is shared, and
    # rfc7234   5.2.2.6.  private    https://tools.ietf.org/html/rfc7234#section-5.2.2.6
    elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*private\s*(,|$)") {
        set beresp.http.X-Cacheable = "H4P;private";
        set beresp.uncacheable = true;
        set beresp.ttl = 119s; // Hit for pass
    }

    # o  the Authorization header field (see Section 4.2 of [RFC7235]) does
    #    not appear in the request, if the cache is shared, unless the
    #    response explicitly allows it (see Section 3.2), and
    # rfc7234   3.2.  Storing Responses to Authenticated Requests    https://tools.ietf.org/html/rfc7234#section-3.2
    elsif (bereq.http.Authorization && beresp.http.Cache-Control !~ "(?i)(^|,)\s*(must-revalidate|public|s-maxage=\d+|dr-maxage=\d+)\s*(?=,|$)") {
        set beresp.http.X-Cacheable = "NO:authorization";
        set beresp.uncacheable = true;
        set beresp.ttl = 0s; // No hit for pass
    }

    # o  the response either:
    # rfc7234   4.2.1.  Calculating Freshness Lifetime  https://tools.ietf.org/html/rfc7234#section-4.2.1
    # A cache can calculate the freshness lifetime (denoted as
    # freshness_lifetime) of a response by using the first match of the
    # following:

    #    o  If the cache is varnish and the v-maxage response directive
    #       is present, use its value, or
    #    *  contains a dr-maxage=ttl[+grace] response directive (custom varnish ttl and grace)
    elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*dr-maxage=\d+(\+\d+)?\s*(,|$)") {
        set beresp.http.X-Cacheable = "YES;"+regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*(dr-maxage=\d+(?:\+\d+)?)\s*(?:,.*)?$", "\1");
        set beresp.ttl = std.duration(regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*dr-maxage=(\d+)(?:\+\d+)?\s*(?:,.*)?$", "\1")+"s", 0s);
        if (beresp.http.Cache-Control ~ "(?i)(^|,)\s*dr-maxage=\d+\+\d+\s*(,|$)") {
            set beresp.http.X-Cacheable = beresp.http.X-Cacheable+";dr;grace="+std.duration(beresp.http.VAR-stale-if-error, 0s);
            set beresp.grace = std.duration(regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*dr-maxage=\d+\+(\d+)\s*(?:,.*)?$", "\1")+"s", 0s);
        }
    }

    #    o  If the cache is shared and the s-maxage response directive
    #       (Section 5.2.2.9) is present, use its value, or
    #    *  contains a s-maxage response directive (see Section 5.2.2.9)
    #       and the cache is shared, or
    # rfc7234   5.2.2.9.  s-maxage    https://tools.ietf.org/html/rfc7234#section-5.2.2.9
    elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*s-maxage=\d+\s*(,|$)") {
        set beresp.http.X-Cacheable = "YES;"+regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*(s-maxage=\d+)\s*(?:,.*)?$", "\1");
        set beresp.ttl = std.duration(regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*s-maxage=(\d+)\s*(?:,.*)?$", "\1")+"s", 0s);
    }

    #    o  If the max-age response directive (Section 5.2.2.8) is present,
    #       use its value, or
    #    *  contains a max-age response directive (see Section 5.2.2.8), or
    # rfc7234   5.2.2.8.  max-age    https://tools.ietf.org/html/rfc7234#section-5.2.2.8
    elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*max-age=\d+\s*(,|$)") {
        set beresp.http.X-Cacheable = "YES;"+regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*(max-age=\d+)\s*(?:,.*)?$", "\1");
        set beresp.ttl = std.duration(regsub(beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*max-age=(\d+)\s*(?:,.*)?$", "\1")+"s", 0s);
    }

    #    o  If the Expires response header field (Section 5.3) is present, use
    #       its value minus the value of the Date response header field, or
    #    *  contains an Expires header field (see Section 5.3), or
    # rfc7234   5.3.  Expires    https://tools.ietf.org/html/rfc7234#section-5.3
    elsif (beresp.http.Expires) {
        if (std.time(beresp.http.Expires, now) > std.time(beresp.http.Date, now)) {
            set beresp.http.X-Cacheable = "YES;"+"expires="+(std.time(beresp.http.Expires, now) - std.time(beresp.http.Date, now));
            set beresp.ttl = std.time(beresp.http.Expires, now) - std.time(beresp.http.Date, now);
        }
        else {
            set beresp.http.X-Cacheable = "H4P;"+"expires=past";
            set beresp.uncacheable = true;
            set beresp.ttl =  119s; // Hit for pass
        }
    }

    #    o  Otherwise, no explicit expiration time is present in the response.
    #       A heuristic freshness lifetime might be applicable; see
    #       Section 4.2.2.

    #    *  contains a Cache Control Extension (see Section 5.2.3) that
    #       allows it to be cached, or
    #elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*extension\s*(,|$)") { }

    ## varnish - default understood status codes
    ## : 200 203         300 301 302 304 307 404     410
    ## rfc7231 - cacheable by default status codes
    ## : 200 203 204 206 300 301             404 405 410 414 501
    ## consider for default : 204 205
    ## consider for freshness: 302 303 307 401 403 405 406 500 501 502 503 504 505
    ## never be cached: 201 202 305 306 402 408 409 411 412 413 415 417
    ##
    ###: custom understood status codes
    ## : 200 203 204 205 206 300 301 302 303 304 307 308
    ## : 400 403 404 405 406 410 414 418
    ## : 500 501 502 503 504 505
    ##
    ###: custom cacheable by default
    ## : 200 203 204 300 301 302 303 304 307 308
    ## : 404 405 410 414 418 500 501 502 503 504

    #    *  has a status code that is defined as cacheable by default (see
    #       Section 4.2.2), or
    # rfc7231   6.1.  Overview of Status Codes    https://tools.ietf.org/html/rfc7231#section-6.1
    # rfc7231 and varnish default status codes : 200 203 204 300 301 302 304 307 404 405 410 414 501
    elsif (regsub(beresp.status, "", "") ~ "^(20[034]|30[01247]|40[45]|41[04]|501)$") {
        if (beresp.http.Last-Modified && std.time(beresp.http.Last-Modified, now) < now) {
            set beresp.http.X-Cacheable = "YES:default=10s<"+regsub((now - std.time(beresp.http.Last-Modified, now)) * 0.1, "\.\d\d\d$", "")+"s<1d";
            if ((now - std.time(beresp.http.Last-Modified, now)) * 0.1 <= 10s) {
                set beresp.ttl = 10s;
            }
            elsif ((now - std.time(beresp.http.Last-Modified, now)) * 0.1 < 1d) {
                set beresp.ttl = (now - std.time(beresp.http.Last-Modified, now)) * 0.1;
            }
            else {
                set beresp.ttl = 1d;
            }
        }
        else {
            set beresp.http.X-Cacheable = "YES:default=119s";
            set beresp.ttl = 119s;
        }
    }
    ## Extended default status codes
    ## : 303 308 418 500 502 503 504
    elsif (regsub(beresp.status, "", "") ~ "^(30[38]|418|50[0234])$") {
        if (beresp.http.Last-Modified && std.time(beresp.http.Last-Modified, now) < now) {
            set beresp.http.X-Cacheable = "YES:extended=10s<"+regsub((now - std.time(beresp.http.Last-Modified, now)) * 0.01, "\.\d\d\d$", "")+"<1h";
            if ((now - std.time(beresp.http.Last-Modified, now)) * 0.01 <= 10s) {
                set beresp.ttl = 10s;
            }
            elsif ((now - std.time(beresp.http.Last-Modified, now)) * 0.01 < 1h) {
                set beresp.ttl = (now - std.time(beresp.http.Last-Modified, now)) * 0.01;
            }
            else {
                set beresp.ttl = 1h;
            }
        }
        else {
            set beresp.http.X-Cacheable = "YES:extended=19s";
            set beresp.ttl = 19s;
        }
    }

    #    *  contains a public response directive (see Section 5.2.2.5).
    # rfc7234   5.2.2.5.  public    https://tools.ietf.org/html/rfc7234#section-5.2.2.5
    elsif (beresp.http.Cache-Control ~ "(?i)(^|,)\s*public\s*(,|$)") {
        if (beresp.http.Last-Modified && std.time(beresp.http.Last-Modified, now) < now) {
            set beresp.http.X-Cacheable = "YES:public=10s<"+regsub((now - std.time(beresp.http.Last-Modified, now)) * 0.1, "\.\d\d\d$", "")+"<1d";
            if ((now - std.time(beresp.http.Last-Modified, now)) * 0.1 <= 10s) {
                set beresp.ttl = 10s;
            }
            elsif ((now - std.time(beresp.http.Last-Modified, now)) * 0.1 < 1d) {
                set beresp.ttl = (now - std.time(beresp.http.Last-Modified, now)) * 0.1;
            }
            else {
                set beresp.ttl = 1d;
            }
        }
        else {
            set beresp.http.X-Cacheable = "YES:public=119s";
            set beresp.ttl = 119s;
        }
    }

    # The rest - no matching rules
    else {
        set beresp.http.X-Cacheable = "NO:others";
        set beresp.uncacheable = true;
        set beresp.ttl = 0s; // No hit for pass
    }

    # Set grace
    if (beresp.http.Cache-Control !~ "(?i)(^|,)\s*dr-maxage=\d+\+\d+\s*(,|$)") {

        # 3.  The stale-while-revalidate Cache-Control Extension    http://tools.ietf.org/html/rfc5861#section-3
        #   When present in an HTTP response, the stale-while-revalidate Cache-
        #   Control extension indicates that caches MAY serve the response in
        #   which it appears after it becomes stale, up to the indicated number
        #   of seconds.
        if (beresp.http.Cache-Control ~ "(?i)(^|,)\s*stale-while-revalidate=\d*\s*(,|$)") {
            set beresp.http.VAR-stale-while-revalidate = std.duration(regsub(
                    beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*stale-while-revalidate=(\d+)\s*(?:,.*)?$", "\1s"), 0s)+"s";
        }

        # 4.  The stale-if-error Cache-Control Extension    http://tools.ietf.org/html/rfc5861#section-4
        #   The stale-if-error Cache-Control extension indicates that when an
        #   error is encountered, a cached stale response MAY be used to satisfy
        #   the request, regardless of other freshness information.
        if (beresp.http.Cache-Control ~ "(?i)(^|,)\s*stale-if-error=\d+\s*(,|$)") {
            set beresp.http.VAR-stale-if-error = std.duration(regsub(
                    beresp.http.Cache-Control, "(?i)^(?:.*,)?\s*stale-if-error=(\d+)\s*(?:,.*)?$", "\1s"), 0s)+"s";
        }

        # Default grace
        if (beresp.http.Last-Modified && std.time(beresp.http.Last-Modified, now) < now) {
            if ((now - std.time(beresp.http.Last-Modified, now)) * 0.5 <= 10s) {
                set beresp.http.VAR-stale-default = "10s";
            }
            elsif ((now - std.time(beresp.http.Last-Modified, now)) * 0.5 < 1d) {
                set beresp.http.VAR-stale-default = ((now - std.time(beresp.http.Last-Modified, now)) * 0.5)+"s";
            }
            else {
                set beresp.http.VAR-stale-default = "86400s";
            }
        }
        else {
            set beresp.http.VAR-stale-default = "59s";
        }

        # Pick largest grace
        if (std.duration(beresp.http.VAR-stale-while-revalidate, 0s) >= std.duration(beresp.http.VAR-stale-if-error, 0s) &&
                std.duration(beresp.http.VAR-stale-while-revalidate, 0s) > std.duration(beresp.http.VAR-stale-default, 0s)) {
            set beresp.http.X-Cacheable = beresp.http.X-Cacheable+";swr;grace="+std.duration(beresp.http.VAR-stale-while-revalidate, 0s);
            set beresp.grace = std.duration(beresp.http.VAR-stale-while-revalidate, 0s);
        }
        elsif (std.duration(beresp.http.VAR-stale-if-error, 0s) >= std.duration(beresp.http.VAR-stale-while-revalidate, 0s) &&
                std.duration(beresp.http.VAR-stale-if-error, 0s) > std.duration(beresp.http.VAR-stale-default, 0s)) {
            set beresp.http.X-Cacheable = beresp.http.X-Cacheable+";sie;grace="+std.duration(beresp.http.VAR-stale-if-error, 0s);
            set beresp.grace = std.duration(beresp.http.VAR-stale-if-error, 0s);
        }
        else {
            set beresp.http.X-Cacheable = beresp.http.X-Cacheable+";def;grace="+std.duration(beresp.http.VAR-stale-default, 0s);
            set beresp.grace = std.duration(beresp.http.VAR-stale-default, 0s);
        }

    }

    # Set cache info for verbose
    set beresp.http.X-Caching = beresp.ttl + "/" + beresp.grace + "/" + beresp.keep + "/" + beresp.uncacheable;

    # cacheable for freshness and stale serving
    if ( ! beresp.uncacheable) {
        set beresp.http.VAR-freshness_lifetime = beresp.ttl+"s";
        if (beresp.ttl == 0s) {
            std.log("cacheable_backend_response_cacheable: ttl=1ms hack for stale serving");
            set beresp.ttl = 1ms;
        }
    }

}


Add: commands for specs, console, replace, replace all

# Voice commands for atom

include folders.vch;
include letters.vch;

Go Strawberry = {Ctrl+Alt+F8};

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

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

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

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

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

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

Find Symbol = {Ctrl+r};

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

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

# --- Editing commands ----------------------------------------
Select Line = {Ctrl+l};
Select (Next = 'Down' | Last = 'Up') 1..20 Lines = {Home} {Shift+$1_$2};
(Join = 'j' | Indent = ']' | Dedent = '[')  That = {Ctrl+$1};
Move Line (Up | Down) 1..20  = {Ctrl+$1_$2};

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

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

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

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

Delete Tags = {Ctrl+Alt+F4};

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

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

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

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

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

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

# Voice commands for atom

include folders.vch;
include letters.vch;

Go Strawberry = {Ctrl+Alt+F8};

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

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

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

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

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

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

Find Symbol = {Ctrl+r};

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

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

# --- Editing commands ----------------------------------------
Select Line = {Ctrl+l};
Select (Next = 'Down' | Last = 'Up') 1..20 Lines = {Home} {Shift+$1_$2};
(Join = 'j' | Indent = ']' | Dedent = '[')  That = {Ctrl+$1};
Move Line (Up | Down) 1..20  = {Ctrl+$1_$2};

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

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

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

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

Delete Tags = {Ctrl+Alt+F4};

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

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

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

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

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


Added newest Atom commands

Added newest Atom commands

# Voice commands for atom

include folders.vch;
include letters.vch;

Find (File | Document) = {Ctrl+t};
Save As = {Ctrl+Shift+s};
Save and Reload =  {Ctrl+s}  Wait(100) {Alt+Tab} Wait(300);

<delimiters> := (Quote = '"' | 'Single Quote' = "'" | Comma = ',' | 'Equal Sign' = '=' | Equals = '='
	| Period = '.' | 'Semi-Colon' = ';');

# --- Navigation commands ------------------------------------
Line 1..200 = {Ctrl+g} $1 {Enter};
(Left = 'b' | Right = 'f') Word = {Alt+$1};
Top = {Ctrl+Home};
Bottom = {Ctrl+End};
(Find = 'Right' | 'Insert After' = 'Right' | 'Insert Before' = 'Left') (Next = 'Enter' | Last = 'Shift+F3') <delimiters>
		= {Ctrl+f} $3 {$2} {Esc}   Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') <delimiters> =  {Ctrl+f} $2 {Enter} {Esc}   Wait(100) {$1};
Fine Alpha <letters> = {Ctrl+f} $1 {Enter} {Esc}   Wait(100) {Right};
Insert (Before = 'Left' | After = 'Right') Alpha <letters> =  {Ctrl+f} $2 {Enter} {Esc}   Wait(100) {$1};
Fine Number 0..9 = {Ctrl+f} $1 {Enter} {Esc} Wait(100) {Right};
Find Again = {F3} {Right};
Find Again 1..20 Times = {F3_$1} {Right};

Find Symbol = {Ctrl+r};

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

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

# --- Editing commands ----------------------------------------
Select (Next = 'Down' | Last = 'Up') 1..20 Lines = {Home} {Shift+$1_$2};
(Join = 'j' | Indent = ']' | Dedent = '[')  That = {Ctrl+$1};
Move Line (Up | Down) 1..20  = {Ctrl+$1_$2};

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

Select between (Delimiters | Brackets) ={Ctrl+m} {Ctrl+Alt+m};
# TO ADD:
# Delete back three words
# delete between next tags

# --- Code commands -------------------------------------------
Start (para = 'p' | Item = 'li' | List = 'ul' | HTML = 'html' | pre = "pre"
	| Snippet = 'snip' | 'Recipe Row' = 'recipe-row') = $1 {Tab};
Start (Header | Heading) 1..7 = h $2 {Tab};
Mid HREF = '">';

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

Shortcut Keys = {Ctrl+Shift+p};

# Voice commands for atom, a fabulous editor

include folders.vch;
include letters.vch;

Find (File | Document) = {Ctrl+t};
Save As = {Ctrl+Shift+s};
Save and Reload =  {Ctrl+s}  Wait(100) {Alt+Tab} Wait(300);

<delimiters> := (Quote = '"' | 'Single Quote' = "'" | Comma = ',' | 'Equal Sign' = '=' | Equals = '=');

# --- Navigation commands ------------------------------------
Line 1..200 = {Ctrl+g} $1 {Enter};
(Left = 'b' | Right = 'f') Word = {Alt+$1};
Top = {Ctrl+Home};
Bottom = {Ctrl+End};

(Find = 'Right' | 'Insert After' = 'Right' | 'Insert Before' = 'Left') (Next = 'Enter' | Last = 'Shift+F3') <delimiters>
		= {Ctrl+f} $3 {$2} {Esc}   Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') <delimiters> =  {Ctrl+f} $2 {Enter} {Esc}   Wait(100) {$1};

Fine Alpha <letters> = {Ctrl+f} $1 {Enter} {Esc}   Wait(100) {Right};
Insert (Before = 'Left' | After = 'Right') Alpha <letters> =  {Ctrl+f} $2 {Enter} {Esc}   Wait(100) {$1};

Find Again = {F3} {Right};
Find Again 1..20 Times = {F3_$1} {Right};

Find Symbol = {Ctrl+r};

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

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

# --- Editing commands ----------------------------------------
Select (Next = 'Down' | Last = 'Up') 1..20 Lines = {Home} {Shift+$1_$2};
Join That = {Ctrl+j};
Move Line (Up | Down) 1..20  = {Ctrl+$1_$2};

Delete Word = {Ctrl+Del};
Delete 1..20 Words = {Ctrl+Del_$1};
(Delete Line | Dede) = {Ctrl+Shift+k};
Delete 1..20 Lines = {Ctrl+Shift+k_$1};

# TO ADD:
# DELETE TWO WORDS
# delete between next tags

# --- Code commands -------------------------------------------
Start (para = 'p' | Item = 'li' | List = 'ul' | HTML = 'html'
	| Snippet = 'snip') = $1 {Tab};
Start (Header | Heading) 1..7 = h $2 {Tab};

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

Shortcut Keys = {Ctrl+Shift+p};


add svg to cache progress.svg in varnish

# This a configuration file for varnish.
# It was generated by plone.recipe.varnish.
# See the vcl(7) man page for details on VCL syntax and semantics.
#
vcl 4.0;

import std;

# configure all backends
backend backend_000 {
   .host = "127.0.0.1";
   .port = "5001";
   .connect_timeout = 0.4s;
   .first_byte_timeout = 300s;
   .between_bytes_timeout  = 60s;
}

sub vcl_init {

}

acl purge {
    "localhost";
    "127.0.0.1";
}

sub vcl_recv {
    set req.backend_hint = backend_000;
    set req.http.grace = "none";

    if (req.method == "PURGE") {
        # Not from an allowed IP? Then die with an error.
        if (!client.ip ~ purge) {
            return (synth(405, "This IP is not allowed to send PURGE requests."));
        }
        return(purge);
    }

    if (req.method == "BAN") {
            # Same ACL check as above:
            if (!client.ip ~ purge) {
            return(synth(403, "Not allowed."));
            }
            #ban("req.url ~ " + req.url);
        ban("req.http.host == " + req.http.host +
            " && req.url == " + req.url);
            # Throw a synthetic page so the
            # request won't go to the backend.
            return(synth(200, "Ban added"));
    }

    if (req.method == "USERINFO" ||
        req.method == "CONNECT" ||
        req.method == "QUIT") {
        return (synth(501, "Not implemented"));
    }

    # Only deal with "normal" types
    if (req.method != "GET" &&
           req.method != "HEAD" &&
           req.method != "PUT" &&
           req.method != "TRACE" &&
           req.method != "OPTIONS" &&
           req.method != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        return(pipe);
    }

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

    if (req.http.Expect) {
        return(pipe);
    }

    if (req.http.If-None-Match && !req.http.If-Modified-Since) {
        return(pass);
    }

    /* Do not cache other authorized content by default */
    if (req.http.Authenticate || req.http.Authorization) {
        return(pass);
    }

    if (req.url ~ "@@registration|@@login|@@resetpassword") {
        return(pass);
    }

    if (req.url ~ "^[^?]*\.(svg|css|js|png|gif|jpeg|jpg|woff|woff2)(\?.*)?$") {
       unset req.http.Cookie;
    }

    if (req.http.Cookie && req.http.Cookie ~ "auth_tkt") {
        return(pass);
    }

    unset req.http.Accept-Encoding;

    return(hash);
}

sub vcl_pipe {
    # By default Connection: close is set on all piped requests, to stop
    # connection reuse from sending future requests directly to the
    # (potentially) wrong backend. If you do want this to happen, you can undo
    # it here.
    # unset bereq.http.connection;

    return(pipe);
}

sub vcl_pass {
    return (fetch);
}

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

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

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

    // Object is in grace, deliver it
    // Automatically triggers a background fetch
    if (obj.ttl + obj.grace > 0s) {
        set req.http.grace = "normal(limited)";
        return (deliver);
    }

    if (req.method == "PURGE") {
        set req.method = "GET";
        set req.http.X-purger = "Purged";
        return(synth(200, "Purged. in hit " + req.url));
    }

    // fetch & deliver once we get the result
    return (fetch);
}

sub vcl_miss {
    if (req.method == "PURGE") {
        set req.method = "GET";
        set req.http.X-purger = "Purged-possibly";
        return(synth(200, "Purged. in miss " + req.url));
    }

    // fetch & deliver once we get the result
    return (fetch);
}

sub vcl_backend_fetch{
    return (fetch);
}

sub vcl_backend_response {
    # The object is not cacheable
    if (beresp.http.Set-Cookie) {
        set beresp.http.X-Cacheable = "NO - Set Cookie";
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
    } elsif (beresp.http.Cache-Control ~ "private") {
        set beresp.http.X-Cacheable = "NO - Cache-Control=private";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    } elsif (beresp.http.Surrogate-control ~ "no-store") {
        set beresp.http.X-Cacheable = "NO - Surrogate-control=no-store";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    } elsif (!beresp.http.Surrogate-Control && beresp.http.Cache-Control ~ "no-cache|no-store") {
        set beresp.http.X-Cacheable = "NO - Cache-Control=no-cache|no-store";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    } elsif (beresp.http.Vary == "*") {
        set beresp.http.X-Cacheable = "NO - Vary=*";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;

    # ttl handling
    } elsif (beresp.ttl < 0s) {
        set beresp.http.X-Cacheable = "NO - TTL < 0";
        set beresp.uncacheable = true;
    } elsif (beresp.ttl == 0s) {
        if (bereq.http.Cookie && bereq.http.Cookie ~ "auth_tkt") {
            set beresp.http.X-Cacheable = "NO - TTL = 0";
            set beresp.uncacheable = true;
        } else {
            set beresp.http.X-Cacheable = "YES, 3600s";
            set beresp.ttl = 3600s;
            set beresp.grace = 36000s;
        }

    # Varnish determined the object was cacheable
    } else {
        set beresp.http.X-Cacheable = "YES";
    }

# https://www.varnish-cache.org/trac/wiki/VCLExampleLongerCaching
    if (beresp.ttl == 31536000s) {  # for deform.js, jquery.form-3.09.js from deformstatic
        /* Remove Expires from backend, it's too long */
        unset beresp.http.expires;

        /* Set the clients TTL on this object */
        set beresp.http.cache-control = "max-age=86400";

        /* Set how long Varnish will keep it */
        set beresp.ttl = 86400s;
    }

    # Do not cache 5xx errors
    if (beresp.status >= 500 && beresp.status < 600) {
        unset beresp.http.Cache-Control;
        set beresp.http.X-Cache = "NOCACHE";
        set beresp.http.Cache-Control = "no-cache, max-age=0, must-revalidate";
        set beresp.ttl = 0s;
        set beresp.http.Pragma = "no-cache";
        set beresp.uncacheable = true;
        return(deliver);
    }

    return (deliver);
}

sub vcl_deliver {
    set resp.http.grace = req.http.grace;
    if (obj.hits > 0) {
         set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
    /* Rewrite s-maxage to exclude from intermediary proxies
      (to cache *everywhere*, just use 'max-age' token in the response to avoid
      this override) */
    if (resp.http.Cache-Control ~ "s-maxage") {
        set resp.http.Cache-Control = regsub(resp.http.Cache-Control, "s-maxage=[0-9]+", "s-maxage=0");
    }
    /* Remove proxy-revalidate for intermediary proxies */
    if (resp.http.Cache-Control ~ ", proxy-revalidate") {
        set resp.http.Cache-Control = regsub(resp.http.Cache-Control, ", proxy-revalidate", "");
    }

    unset resp.http.Server;

}

/*
 We can come here "invisibly" with the following errors: 413, 417 & 503
*/
sub vcl_synth {
    set resp.http.Content-Type = "text/html; charset=utf-8";
    set resp.http.Retry-After = "5";

    synthetic( {"
        <?xml version="1.0" encoding="utf-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

            <title>Varnish cache server: "} + resp.status + " " + resp.reason + {" </title>

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

"} + resp.reason + {"

Guru Meditation:

XID: "} + req.xid + {"

Varnish cache server

    "} );

    return (deliver);
}

# This a configuration file for varnish.
# It was generated by plone.recipe.varnish.
# See the vcl(7) man page for details on VCL syntax and semantics.
#
vcl 4.0;

import std;

# configure all backends
backend backend_000 {
   .host = "127.0.0.1";
   .port = "5001";
   .connect_timeout = 0.4s;
   .first_byte_timeout = 300s;
   .between_bytes_timeout  = 60s;
}

sub vcl_init {

}

acl purge {
    "localhost";
    "127.0.0.1";
}

sub vcl_recv {
    set req.backend_hint = backend_000;
    set req.http.grace = "none";

    if (req.method == "PURGE") {
        # Not from an allowed IP? Then die with an error.
        if (!client.ip ~ purge) {
            return (synth(405, "This IP is not allowed to send PURGE requests."));
        }
        return(purge);
    }

    if (req.method == "BAN") {
            # Same ACL check as above:
            if (!client.ip ~ purge) {
            return(synth(403, "Not allowed."));
            }
            #ban("req.url ~ " + req.url);
        ban("req.http.host == " + req.http.host +
            " && req.url == " + req.url);
            # Throw a synthetic page so the
            # request won't go to the backend.
            return(synth(200, "Ban added"));
    }

    if (req.method == "USERINFO" ||
        req.method == "CONNECT" ||
        req.method == "QUIT") {
        return (synth(501, "Not implemented"));
    }

    # Only deal with "normal" types
    if (req.method != "GET" &&
           req.method != "HEAD" &&
           req.method != "PUT" &&
           req.method != "TRACE" &&
           req.method != "OPTIONS" &&
           req.method != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        return(pipe);
    }

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

    if (req.http.Expect) {
        return(pipe);
    }

    if (req.http.If-None-Match && !req.http.If-Modified-Since) {
        return(pass);
    }

    /* Do not cache other authorized content by default */
    if (req.http.Authenticate || req.http.Authorization) {
        return(pass);
    }

    if (req.url ~ "@@registration|@@login|@@resetpassword") {
        return(pass);
    }

    if (req.url ~ "^[^?]*\.(css|js|png|gif|jpeg|jpg|woff|woff2)(\?.*)?$") {
       unset req.http.Cookie;
    }

    if (req.http.Cookie && req.http.Cookie ~ "auth_tkt") {
        return(pass);
    }

    unset req.http.Accept-Encoding;

    return(hash);
}

sub vcl_pipe {
    # By default Connection: close is set on all piped requests, to stop
    # connection reuse from sending future requests directly to the
    # (potentially) wrong backend. If you do want this to happen, you can undo
    # it here.
    # unset bereq.http.connection;

    return(pipe);
}

sub vcl_pass {
    return (fetch);
}

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

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

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

    // Object is in grace, deliver it
    // Automatically triggers a background fetch
    if (obj.ttl + obj.grace > 0s) {
        set req.http.grace = "normal(limited)";
        return (deliver);
    }

    if (req.method == "PURGE") {
        set req.method = "GET";
        set req.http.X-purger = "Purged";
        return(synth(200, "Purged. in hit " + req.url));
    }

    // fetch & deliver once we get the result
    return (fetch);
}

sub vcl_miss {
    if (req.method == "PURGE") {
        set req.method = "GET";
        set req.http.X-purger = "Purged-possibly";
        return(synth(200, "Purged. in miss " + req.url));
    }

    // fetch & deliver once we get the result
    return (fetch);
}

sub vcl_backend_fetch{
    return (fetch);
}

sub vcl_backend_response {
    # The object is not cacheable
    if (beresp.http.Set-Cookie) {
        set beresp.http.X-Cacheable = "NO - Set Cookie";
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
    } elsif (beresp.http.Cache-Control ~ "private") {
        set beresp.http.X-Cacheable = "NO - Cache-Control=private";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    } elsif (beresp.http.Surrogate-control ~ "no-store") {
        set beresp.http.X-Cacheable = "NO - Surrogate-control=no-store";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    } elsif (!beresp.http.Surrogate-Control && beresp.http.Cache-Control ~ "no-cache|no-store") {
        set beresp.http.X-Cacheable = "NO - Cache-Control=no-cache|no-store";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    } elsif (beresp.http.Vary == "*") {
        set beresp.http.X-Cacheable = "NO - Vary=*";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;

    # ttl handling
    } elsif (beresp.ttl < 0s) {
        set beresp.http.X-Cacheable = "NO - TTL < 0";
        set beresp.uncacheable = true;
    } elsif (beresp.ttl == 0s) {
        if (bereq.http.Cookie && bereq.http.Cookie ~ "auth_tkt") {
            set beresp.http.X-Cacheable = "NO - TTL = 0";
            set beresp.uncacheable = true;
        } else {
            set beresp.http.X-Cacheable = "YES, 3600s";
            set beresp.ttl = 3600s;
            set beresp.grace = 36000s;
        }

    # Varnish determined the object was cacheable
    } else {
        set beresp.http.X-Cacheable = "YES";
    }

# https://www.varnish-cache.org/trac/wiki/VCLExampleLongerCaching
    if (beresp.ttl == 31536000s) {  # for deform.js, jquery.form-3.09.js from deformstatic
        /* Remove Expires from backend, it's too long */
        unset beresp.http.expires;

        /* Set the clients TTL on this object */
        set beresp.http.cache-control = "max-age=86400";

        /* Set how long Varnish will keep it */
        set beresp.ttl = 86400s;
    }

    # Do not cache 5xx errors
    if (beresp.status >= 500 && beresp.status < 600) {
        unset beresp.http.Cache-Control;
        set beresp.http.X-Cache = "NOCACHE";
        set beresp.http.Cache-Control = "no-cache, max-age=0, must-revalidate";
        set beresp.ttl = 0s;
        set beresp.http.Pragma = "no-cache";
        set beresp.uncacheable = true;
        return(deliver);
    }

    return (deliver);
}

sub vcl_deliver {
    set resp.http.grace = req.http.grace;
    if (obj.hits > 0) {
         set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
    /* Rewrite s-maxage to exclude from intermediary proxies
      (to cache *everywhere*, just use 'max-age' token in the response to avoid
      this override) */
    if (resp.http.Cache-Control ~ "s-maxage") {
        set resp.http.Cache-Control = regsub(resp.http.Cache-Control, "s-maxage=[0-9]+", "s-maxage=0");
    }
    /* Remove proxy-revalidate for intermediary proxies */
    if (resp.http.Cache-Control ~ ", proxy-revalidate") {
        set resp.http.Cache-Control = regsub(resp.http.Cache-Control, ", proxy-revalidate", "");
    }

    unset resp.http.Server;

}

/*
 We can come here "invisibly" with the following errors: 413, 417 & 503
*/
sub vcl_synth {
    set resp.http.Content-Type = "text/html; charset=utf-8";
    set resp.http.Retry-After = "5";

    synthetic( {"
        <?xml version="1.0" encoding="utf-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

            <title>Varnish cache server: "} + resp.status + " " + resp.reason + {" </title>

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

"} + resp.reason + {"

Guru Meditation:

XID: "} + req.xid + {"

Varnish cache server

    "} );

    return (deliver);
}