Posted in: , ,
I'm playing with a small web based RSS reader, and one of the things it does is cache a lot of data to reduce the impact on the sites I follow feeds from. However I realized this a couple of days ago I'd completely forgotten to set cache headers, and I kept hammering my own site.

That wouldn't do, and since my app is using Rack to interface to the webserver, there's a simple solution: Write a tiny Rack middleware class, just like I described in "Rewriting content types with Rack".

The great thing is that because of Rack this class can be used to add cache headers for anything from pure Rack based apps to apps using Rails, Merb, or any number of other Ruby frameworks that has Rack adapters. If you haven't looked into Rack already, do.

I added the following in a file named "cachesettings.rb":

class CacheSettings
  def initialize app, pat
    @app = app
    @pat = pat

def call env res = path = env["REQUEST_PATH"] @pat.each do |pattern,data| if path =~ pattern res[1]["Cache-Control"] = data[:cache_control] if data.has_key?(:cache_control) res[1]["Expires"] = ( + data[:expires]).utc.rfc2822 if data.has_key?(:expires) return res end end res end end

It's pretty straightforward:

  • On setup it stores a hash of config information
  • On each call it first executes the next step in the chain.
  • Then it iterates over the hash and if there's a match it optionally adds Cache-Control: and Expires: headers. For the Expires: header the configuration info is the number of seconds from "now".

I then added it to my (the Rack config file for my app) like this:

require 'cachesettings'

use CacheSettings, { /\/static\// => { :cache_control => "max-age=86400, public", :expires => 86400 } } use Rack::Static, :urls => ["/static"]

That's all there's too it - it can add cache headers to any arbitrary results. It should be obvious how to extend this to add any arbitrary headers too, or rewrite existing headers.

Useful exercises for the reader: Extend it to allow adding a Last-Modified header or Etag's.

blog comments powered by Disqus