If it won't be simple, it simply won't be. [Hire me, source code] by Miki Tebeka, CEO, 353Solutions

Saturday, August 15, 2009

Dynamicall Generating Static Content

Sometimes you want to generate a resource (say a chart) once and then keep it. This way you don't need to keep track of all the objects you've created and can easily clean (since if someone want the resource again it will be generated again).

One way is to do it with lighttpd 404 handlers (view complete code).

lighttpd.conf

# lighttpd configuration file
server.modules = ( "mod_cgi" )

# This is generated by by web-up
include "lighttpd.inc"

server.document-root = var.root
server.pid-file = var.root + "/lighttpd.pid"

# files to check for if .../ is requested
index-file.names = ( "index.cgi" )

## set the event-handler (read the performance section in the manual)
# server.event-handler = "freebsd-kqueue" # needed on OS X

# mimetype mapping
mimetype.assign = (
".pdf" => "application/pdf",
".sig" => "application/pgp-signature",
".spl" => "application/futuresplash",
".class" => "application/octet-stream",
".ps" => "application/postscript",
".torrent" => "application/x-bittorrent",
".dvi" => "application/x-dvi",
".gz" => "application/x-gzip",
".pac" => "application/x-ns-proxy-autoconfig",
".swf" => "application/x-shockwave-flash",
".tar.gz" => "application/x-tgz",
".tgz" => "application/x-tgz",
".tar" => "application/x-tar",
".zip" => "application/zip",
".mp3" => "audio/mpeg",
".m3u" => "audio/x-mpegurl",
".wma" => "audio/x-ms-wma",
".wax" => "audio/x-ms-wax",
".ogg" => "application/ogg",
".wav" => "audio/x-wav",
".gif" => "image/gif",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".png" => "image/png",
".xbm" => "image/x-xbitmap",
".xpm" => "image/x-xpixmap",
".xwd" => "image/x-xwindowdump",
".css" => "text/css",
".html" => "text/html",
".htm" => "text/html",
".js" => "text/javascript",
".asc" => "text/plain",
".c" => "text/plain",
".cpp" => "text/plain",
".log" => "text/plain",
".conf" => "text/plain",
".text" => "text/plain",
".txt" => "text/plain",
".dtd" => "text/xml",
".xml" => "text/xml",
".mpeg" => "video/mpeg",
".mpg" => "video/mpeg",
".mov" => "video/quicktime",
".qt" => "video/quicktime",
".avi" => "video/x-msvideo",
".asf" => "video/x-ms-asf",
".asx" => "video/x-ms-asf",
".wmv" => "video/x-ms-wmv",
".bz2" => "application/x-bzip",
".tbz" => "application/x-bzip-compressed-tar",
".tar.bz2" => "application/x-bzip-compressed-tar"
)


$HTTP["url"] =~ "\.pdf$" {
server.range-requests = "disable"
}

# which extensions should not be handle via static-file transfer
static-file.exclude-extensions = ( ".cgi" )

# This is where the magic happens :)
$HTTP["url"] =~ "/charts/.*\.png$" {
server.error-handler-404 = "/chart.cgi"
}

chart.cgi

#!/usr/bin/env python
'''This CGI script is called by lighttpd 404 error handler when it tries to find
/charts/XXX.png.

This way the image is generated once and then we let lighttpd serve it as static
content.
'''

# The below is needed to tell pylab to work without a screen
import matplotlib; matplotlib.use("Agg")
from pylab import hist, randn, title, savefig

from os import environ
from os.path import basename
from urlparse import urlparse

def generate_chart(name, outfile):
# Generate a random chart
hist(randn(1000), 100)
title("Chart for \"%s\"" % name)
savefig(outfile)

def main():
import cgitb; cgitb.enable()

o = urlparse(environ.get("REQUEST_URI", ""))
if not o.path:
print "Content-type: text/plain\n"
print "ERROR: Can't find path"
raise SystemExit

outfile = "charts/%s" % basename(o.path)
name = basename(o.path).replace(".png", "")
if not name:
print "Content-type: text/plain\n"
print "ERROR: 'name' not specified"
raise SystemExit

generate_chart(name, outfile)
image = open(outfile, "rb").read()

print "Content-type: image/png\n"
print image

if __name__ == "__main__":
main()

No comments:

Blog Archive