# I have tried to implement the same functionality as cgi_buffer. # http://www.mnot.net/cgi_buffer/ # Author: Francisco Javier Cabello # spyce file: # -----------------------o----------------------- # [[.import name=etag]] # # Just a test # # -----------------------o----------------------- # When you import etag module a http header (ETag header)is added to the output # of your spyce script. The next time your browser ask for the same page to the # server, it will send the header 'If-None-Match'. The server will check the # etag given and the one it has computed and when both values will be the same, # it will send a 'Not Modified' http response. # Example: # First time: # ----------------------- # GET /cgi-bin/main/main.spy HTTP/1.1 # Host: 192.67.79.171 # User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6) Gecko/20040227 # Firefox/0.8 # Accept: # text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 # Accept-Language: en-us,en;q=0.5 # Accept-Encoding: gzip,deflate # Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 # Keep-Alive: 300 # Connection: keep-alive # Cookie: current_page=main/300_Mstate; user=administrator; # session=7ef88d81a0ce8d6d7cf8a8d7c01d7498; language=en # HTTP/1.1 200 Ok # Date: Wed, 14 Jul 2004 13:53:14 GMT # Server: Apache/1.3.27 (Unix) Debian GNU/Linux mod_gzip/1.3.26.1a # mod_python/2.7.8 Python/2.2.2 # X-Spyce: Spyce/modpy_1.3.12 Python/2.2 # ETag: "CTjpbS6wVaFs7SuUOMx8uQ==" # Keep-Alive: timeout=15, max=91 # Connection: Keep-Alive # Content-Type: text/html; charset=iso-8859-1 # ... page ... # ----------------------- # Second time: # ----------------------- # GET /cgi-bin/main/main.spy HTTP/1.1 # Host: 192.67.79.171 # User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6) Gecko/20040227 # Firefox/0.8 # Accept: # text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 # Accept-Language: en-us,en;q=0.5 # Accept-Encoding: gzip,deflate # Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 # Keep-Alive: 300 # Connection: keep-alive # Cookie: current_page=main/300_Mstate; user=administrator; # session=7ef88d81a0ce8d6d7cf8a8d7c01d7498; language=en # If-None-Match: "CTjpbS6wVaFs7SuUOMx8uQ==" # Cache-Control: max-age=0 # HTTP/1.1 304 Not Modified # Date: Wed, 14 Jul 2004 13:53:14 GMT # Server: Apache/1.3.27 (Unix) Debian GNU/Linux mod_gzip/1.3.26.1a # mod_python/2.7.8 Python/2.2.2 # X-Spyce: Spyce/modpy_1.3.12 Python/2.2 # ETag: "CTjpbS6wVaFs7SuUOMx8uQ==" # Keep-Alive: timeout=15, max=91 # Connection: Keep-Alive # Content-Type: text/html; charset=iso-8859-1 # ----------------------- from spyceModule import spyceModule from cStringIO import StringIO import base64, md5 OUTPUT_POSITION = 94 __doc__ = '''ETag module. provides etag header generation and verification.''' class etag(spyceModule): def start(self): # install compress filter into response module self._filter = FilterEtag(self) self._api.getModule('response').addFilter(OUTPUT_POSITION, self._filter) def init(self): pass def finish(self, theError=None): if not theError: self._filter.close() class FilterEtag(Filter): def __init__(self, module): self._module = module self._buf = StringIO() self._flushed = 0 def writeStatic(self, s): self.write(s) def writeExpr(self, s, **kwargs): self.write(str(s)) def write(self, s, *args, **kwargs): self._buf.write(s) def flushImpl(self, final=0): self._flushed = 1 body = self._buf.getvalue() self._buf = StringIO() etag = "" if final: # etag = base64(md5(body)) etag = base64.encodestring(md5.new(str(body)).digest())[:-1] self._module._api.getModule('response').addHeader('ETag', '"%s"' % str(etag) ) if_none_match = self._module._api.getModule('request').getHeader('If-None-Match') if not if_none_match or string.find(if_none_match, etag)<0: # if If-None-Match header hasn't been found or etag sent is different from # etag computed self.next.write(body) else: # etag sent is the same self._module._api.getModule('response').setReturnCode( 304 ) else: self.next.write(body) etag_sent = self._module._api.getModule('request').getHeader('If-None-Match') def clearImpl(self): self._buf = StringIO() def close(self): self.flushImpl(1)