Saturday, January 21, 2012

HTTP caching in Google App Engine (Python)

I wanted to add some HTTP caching in the browser on the Google App Engine, didn't see much for write ups, here is what I did

Static Content

Static content is straight forward, you can reference the following in the configuration documents: Static File Handlers. This will set the basic static content expiry, for example:
default_expiration: "1d"
This sets the default for all static content, and then per URL file handler:
- url: /static
  static_dir: static
  expiration: "1d"
This will define a per handler expiration. In my case, they are the same.

Response Caching

For any request to your application, you might want to cache that response in request handler implementation. To do so, you can add the response headers as follows:
self.response.headers["Expires"] = util.get_expiration_stamp(60)
self.response.headers["Content-Type"] = "application/json"
self.response.headers["Cache-Control: max-age"] = 60
self.response.headers["Cache-Control"] = "public"
self.response.out.write(template.render(_path, _template_values))
Note the expires implementation, this needs to be a valid date formatted string. The following is the implementation:
class GMT(datetime.tzinfo):
    def utcoffset(self, dt):
        return datetime.timedelta(hours=10) # + self.dst(dt)
    def tzname(self, dt):
        return "GMT"
    def dst(self, dt):
        return datetime.timedelta(0) 

class Util(object):
    
    def get_expiration_stamp(self,seconds):
        gmt = GMT() 
        delta = datetime.timedelta(seconds=seconds)
        expiration = self.get_current_time()
        expiration = expiration.replace(tzinfo=gmt) 
        expiration = expiration + delta
        EXPIRATION_MASK = "%a, %d %b %Y %H:%M:%S %Z"
        
        return expiration.strftime(EXPIRATION_MASK)
See Also:

2 comments:

Riaan Jacobs said...

Thank you, out of all the posts I've read about caching on App Engine this one was the most useful and succinct.

Brett Dusek said...

Thanks, this helped a lot for setting expiration on the cache files of my directory.

I haven't yet tried your response cache, but because I'm unsure how to implement your code example. I'm entirely new to python and GAE. My assumption (based off my own example) would look something like:

(app.yaml)
application: nwstatic
version: 1
runtime: python
api_version: 1

default_expiration: "7d"

handlers:
- url: /img
static_dir: img

- url: /css
static_dir: css

- url: .*
script: example-response-cache.py


(example-response-cache.py)

self.response.headers["Expires"] = util.get_expiration_stamp(60)
self.response.headers["Content-Type"] = "application/json"
self.response.headers["Cache-Control: max-age"] = 60
self.response.headers["Cache-Control"] = "public"
self.response.out.write(template.render(_path, _template_values))

class GMT(datetime.tzinfo):
def utcoffset(self, dt):
return datetime.timedelta(hours=10) # + self.dst(dt)
def tzname(self, dt):
return "GMT"
def dst(self, dt):
return datetime.timedelta(0)

class Util(object):

def get_expiration_stamp(self,seconds):
gmt = GMT()
delta = datetime.timedelta(seconds=seconds)
expiration = self.get_current_time()
expiration = expiration.replace(tzinfo=gmt)
expiration = expiration + delta
EXPIRATION_MASK = "%a, %d %b %Y %H:%M:%S %Z"

return expiration.strftime(EXPIRATION_MASK)


---------------------

Is this correct?

Share on Twitter