pod afColdFeet

An asset caching strategy for your Bed Application

Mixins

ColdFeetConfigIds

IoC Config values for Cold Feet.

DigestStrategy

(Service) - Cold Feet's strategy for generating digests from a file.

UrlExclusions

(Service) - Contribute regular expressions to exclude URLs from being handled / altered by ColdFeet.

UrlTransformer

(Service) - ColdFeet's strategy for transforming URLs.

Classes

Adler32Digest

DigestStrategy that returns a (url-safe) Base64 encoded CRC calculated with the Adler32 algorithm.

AppVersionDigest

DigestStrategy that returns the application's version number in production, and a random string in development.

FixedValueDigest

DigestStrategy that returns a constant value - use during testing.

NameTransformer

UrlTransformer that generates ColdFeet URLs in the format /css/myStyle.coldFeet.XXXX.css

PathTransformer

UrlTransformer that generates ColdFeet URLs in the format /coldFeet/XXXX/css/myStyle.css

Overview

Cold Feet is an asset caching strategy for your Bed App.

  • Tired of telling clients to clear their browser cache to pick up the latest CSS and Javascript changes?
  • Don't want lame browser cache busting because you need network performance?
  • Then get Cold Feet!

Install

Install Cold Feet with the Fantom Repository Manager ( fanr ):

C:\> fanr install -r http://repo.status302.com/fanr/ afColdFeet

To use in a Fantom project, add a dependency to build.fan:

depends = ["sys 1.0", ..., "afColdFeet 1.3"]

Documentation

Full API & fandocs are available on the Status302 repository.

Quick Start

  1. Create a text file called Example.fan:
    using afIoc
    using afBedSheet
    using afColdFeet
    
    class Example {
        @Inject PodHandler? podHandler
    
        Text coldFeetUrls() {
            asset := podHandler.fromPodResource(`fan://icons/x256/flux.png`)
            msg   := "   Normal URL: ${asset.localUrl} \n"
            msg   += "Cold Feet URL: ${asset.clientUrl}\n"
            return Text.fromPlain(msg)
        }
    }
    
    @SubModule { modules=[ColdFeetModule#] }
    class AppModule {
        @Contribute { serviceType=Routes# }
        static Void contributeRoutes(Configuration conf) {
            conf.add(Route(`/`, Example#coldFeetUrls))
        }
    }
    
    class Main {
        Int main() {
            BedSheetBuilder(AppModule#.qname).startWisp(8080)
        }
    }
  2. Run Example.fan as a Fantom script from the command line. This starts the BedSheet app server:
    C:\> fan Example.fan
       ___    __                 _____        _
      / _ |  / /_____  _____    / ___/__  ___/ /_________  __ __
     / _  | / // / -_|/ _  /===/ __// _ \/ _/ __/ _  / __|/ // /
    /_/ |_|/_//_/\__|/_//_/   /_/   \_,_/__/\__/____/_/   \_, /
               Alien-Factory BedSheet v1.4.8, IoC v2.0.6 /___/
    
    IoC Registry built in 612ms and started up in 104ms
    
    Bed App 'Unknown' listening on http://localhost:8080/
  3. Visit http://localhost:8080/
    .  Normal URL: /pods/icons/x256/flux.png
    Cold Feet URL: /coldFeet/infYBQ==/pods/icons/x256/flux.png

Usage

Contribute your asset directories to the BedSheet FileHandler service as usual, but rather than hard-coding the asset URLs, let the FileHandler service generate them for you.

Cold Feet invisibly instruments the FileHandler service to add a caching strategy to the generated client URLs. An asset URL such as `/css/myStyles.css` magically becomes a Cold Feet URL like `/coldFeet/XXXX/css/myStyles.css`, where:

  • /coldFeet is a prefix used to identify the URL on incoming requests.
  • /XXXX is a digest, generated by Cold Feet, that changes when the asset content changes.

When a request is made for the asset using the modified URL, Cold Feet intercepts the request using BedSheet middleware and serves up the file.

Cold Feet lets the browser aggressively cache these files by setting a far-future expiration header on it (1 year by default). Note this expiration header is only enabled in production; see IoC Env.

If during that 1 year the asset is modified then the Cold Feet URL will change, just as the XXXX digest changes. This forces the browser to download the new asset.

The smart ones amongst you will be asking, "But what if the browser requests an old asset URL?" Simple, Cold Feet recognises outdated URLs and responds with a 308 - Moved Permanently redirecting the browser to the new asset URL.

What Usage?

Because Cold Feet works in the background, invisibly advising the FileHandler and PodHandler services, there is no Cold Feet Usage per se. Just add Cold Feet as a project dependency and optionally configure a digest.

Then use FileHandler and PodHandler to generate client URLs to your files, and use them your web pages.

You may also prevent ColdFeet from altering subsets of URLs by contributing regular expressions to UrlExclusions. Example, to make ColdFeet ignore all files in the directory images/:

@Contribute { serviceType=UrlExclusions# }
static Void contributeUrlExclusions(Configuration config) {
    config.add("^/images/".toRegex)
}

Digest Strategies

Adler-32

This is the default strategy used by Cold Feet. It calculates a digest with the Adler-32 checksum algorithm. The checksum is then Base-64 encoded using the URL and filename safe alphabet.

The Adler-32 checksum was designed for speed and created for use in the zlib compression library which makes it an ideal fit for an asset digest.

Note that if your CSS file has a relative URL in it, such as:

.pretty-div {
    background-image: url(../images/pretty.png);
}

Then, under Adler-32 and path transformations, pretty.png will be served under the wrong digest. This is because if your CSS file is served under:

/coldFeet/x9x9x9/css/styles.css

The browser will resolve pretty.png to be under the same digest path:

/coldFeet/x9x9x9/css/../images/pretty.png

This won't do any harm, as Cold Feet will simply redirect the browser to the correct URL, but the redirect is a wasted round trip. As each redirect creates a warning, monitor your logs to find any missed relative URLs and correct as necessary. If there are many, you may wish to preprocess your CSS files to generate the Cold Feet URLs before serving.

App Version

This simple strategy ignores the asset file in question and instead returns the application's pod version. Thus, when a new application is deployed (with an new version), clients will re-download all the assets.

When not in production mode, the digest defaults to a random string.

To use, override the default digest strategy in your AppModule:

class AppModule {
    @Override
    static DigestStrategy overrideDigestStrategy() {
        AppVersionDigest()
    }
}

Fixed Value

Use this strategy in testing. It returns a fixed / constant value each and every time.

To use, override the default digest strategy in your AppModule:

class AppModule {
    @Override
    static DigestStrategy overrideDigestStrategy() {
        FixedValueDigest("XXX")
    }
}

URL Transforming

You may customise the way URLs are transformed to and from ColdFeet URLs. To do so, implement and contribute a UrlTransformer.

Path Transformer

This is the default URL transformation as detailed in the rest of the documentation. The ColdFeet prefix and digest are prepended to the URL as path segments:

/css/myStyle.css  --> /coldFeet/XXXX/css/myStyle.css

Name Transformer

If you don't like the idea of Cold Feet transforming the paths of your URLs, try transforming the name instead!

The NameTransformer inserts the ColdFeet prefix and digest into the name part of the URL, leaving the path alone:

/css/myStyle.css  --> /css/myStyle.coldFeet.XXXX.css

To use, override the default URL transformer in your AppModule:

class AppModule {
    @Override
    static UrlTransformer overrideUrlTransformer(Registry registry) {
        registry.autobuild(NameTransformer#)
    }
}

Release Notes

v1.3.2

  • Chg: Advice for FileAssetCache no longer Err's if the file doesn't exist. (New in BedSheet 1.4.8)
  • Chg: Added ColdFeetMiddleware to BedSheet's StackFrameFilter because it's boring!

v1.3.0

  • New: Added UrlTransformer service so ColdFeet URLs maybe anything you like!
  • New: NameTransformer as an alternative to transforming URL paths.
  • Chg: Format of contributed urlPrefix is no longer a /uri/ but a simple string. (Potential breaking change.)

v1.2.8

  • New: Contribute regular expressions to UrlExclusions to exclude URLs from being handled / altered by ColdFeet.

v1.2.6

v1.2.4

  • New: Cold Feet now also provides an asset caching strategy for pod resources.
  • Chg: Updated to use IoC 1.7.6 and BedSheet 1.3.14.

v1.2.2

v1.2.0

  • New: Cold Feet now invisibly instruments / advises BedSheet's FileHandler service so it returns asset caching URLs!
  • Chg: Removed ColdFeet service as it is no longer needed. Use BedSheet's FileHandler instead.
  • Chg: Updated to use BedSheet 1.3.10.

v1.1.4

  • New: Adler-32 digests are cached so they aren't re-calculated on every request. (Uses BedSheet's new FileMetaCache service.)
  • Chg: Updated to use BedSheet 1.3.8.

v1.1.2

  • Chg: Updated to use IoC 1.6.0 and BedSheet 1.3.6.
  • Bug: Urls with query params were not accepted.
  • Bug: Corrected problems with redirecting on incorrect digest.

v1.1.0

  • New: Added the Alder-32 digest strategy.
  • Chg: Renamed ChecksumStrategy -> DigestStrategy and renamed the implmentations to suit.
  • Chg: Renamed the ColdFeet service methods to match those in BedSheet's FileHandler.
  • Chg: All redirects are logged as warnings (complete with the referer). This allows you to find any missed relative references.

v1.0.0

  • New: Added assetExpiresIn config value - defaults to 10 years.
  • Chg: Uses new FileHandler methods in BedSheet 1.3.4.

v0.0.2

  • New: Preview Release