"""Bobo handler module For use with medusa & python object publisher copyright 1997 amos latteier (code based on script_handler.py) Use: here is a sample fragment from a script to start medusa: ... hs = http_server.http_server (IP_ADDRESS, HTTP_PORT, rs, lg) ... sys.path.insert(0,'c:\\windows\\desktop\\medusa2\\www\\bobo') import bobotest bh=bobo_handler.bobo_handler(bobotest, debug=1) #create bobo handler hs.install_handler(bh) #install handler in http server ... This will install the bobo handler on the http server and give you access to the bobotest module via urls like this: http://myserver.com/bobotest/blah/blah bobo_handler initalization options: * debug: If the debug flag is set then the published module will be reloaded whenever its source is changed on disk. This is very handy for developement. * uri_base: If the uri_base isn't specified it defaults to / """ __version__="1.03" import cgi_module_publisher import sys import regex import string import os try: from cStringIO import StringIO except: from StringIO import StringIO try: import thread mutex = thread.allocate_lock() THREADS = 1 except: THREADS = 0 import counter import default_handler import producers split_path = default_handler.split_path unquote = default_handler.unquote get_header = default_handler.get_header CONTENT_LENGTH = regex.compile ('Content-Length: \([0-9]+\)', regex.casefold) # maps request headers to environment variables # header2env={'Content-Length' : 'CONTENT_LENGTH', 'Content-Type' : 'CONTENT_TYPE', 'Referer' : 'HTTP_REFERER', 'User-Agent' : 'HTTP_USER_AGENT', 'Accept' : 'HTTP_ACCEPT', 'Accept-Charset' : 'HTTP_ACCEPT_CHARSET', 'Accept-Language' : 'HTTP_ACCEPT_LANGUAGE', 'Host' : None, 'Connection' : 'CONNECTION_TYPE', 'Pragma' : None, 'Authorization' : 'HTTP_AUTHORIZATION', 'Cookie' : 'HTTP_COOKIE', } # convert keys to lower case for case-insensitive matching # for (key,value) in header2env.items(): del header2env[key] key=string.lower(key) header2env[key]=value class bobo_handler: "publishes a module via bobo" def __init__ (self, module, uri_base=None, debug=None): self.module = module self.debug = debug if self.debug: self.last_reload=self.module_mtime() self.hits = counter.counter() # if uri_base is unspecified, assume it # starts with the published module name # if not uri_base: uri_base="/%s" % module.__name__ elif uri_base[-1]=="/": # kill possible trailing / uri_base=uri_base[:-1] self.uri_base=uri_base uri_regex='%s.*' % self.uri_base self.uri_regex = regex.compile(uri_regex) def match (self, request): uri = request.uri if self.uri_regex.match (uri) == len(uri): return 1 else: return 0 def handle_request (self, request): [path, params, query, fragment] = split_path (request.uri) while path and path[0] == '/': path = path[1:] if '%' in path: path = unquote (path) self.hits.increment() if query: # cgi_publisher_module doesn't want the leading '?' query = query[1:] self.env = {} self.env['REQUEST_METHOD'] = string.upper(request.command) self.env['SERVER_PORT'] = '%s' % request.channel.server.port self.env['SERVER_NAME'] = request.channel.server.server_name self.env['SERVER_SOFTWARE'] = request['Server'] self.env['SCRIPT_NAME'] = self.uri_base # are script_name and path_info ok? self.env['QUERY_STRING'] = query try: path_info=string.split(path,self.uri_base[1:],1)[1] except: path_info='' self.env['PATH_INFO'] = path_info self.env['GATEWAY_INTERFACE']='CGI/1.1' # what should this really be? self.env['REMOTE_ADDR']=request.channel.addr[0] self.env['REMOTE_HOST']=request.channel.addr[0] #what should this be? for header in request.header: [key,value]=string.split(header,": ",1) key=string.lower(key) if header2env.has_key(key): if header2env[key]: self.env[header2env[key]]=value else: key='HTTP_'+string.upper(string.join(string.split(key,"-"),"_")) self.env[key]=value # remove empty environment variables # for key in self.env.keys(): if self.env[key]=="" or self.env[key]==None: del self.env[key] if request.command in ["post","put"]: request.collector=input_collector(self,request) request.channel.set_terminator (None) else: sin=StringIO('') self.continue_request(sin,request) def continue_request(self,sin,request): "continue handling request now that we have the stdin" # if we have threads spawn a new one to publish the module # so we dont freeze the server while publishing. if THREADS: thread.start_new_thread(self._continue_request,(sin,request)) else: self._continue_request(sin,request) def _continue_request(self,sin,request): "continue handling request now that we have the stdin" sout = StringIO() serr = StringIO() if self.debug: m_time=self.module_mtime() if m_time> self.last_reload: reload(self.module) self.last_reload=m_time if THREADS: mutex.acquire() cgi_module_publisher.publish_module( self.module.__name__, stdin=sin, stdout=sout, stderr=serr, environ=self.env, #debug=1 ) if THREADS: mutex.release() if serr.tell(): request.log(serr.getvalue()) response=sout response=response.getvalue() # set response headers [headers,html]=string.split(response,"\n\n",1) headers=string.split(headers,"\n") for line in headers: [header, header_value]=string.split(line,": ",1) if header=="Status": [code,message]=string.split(header_value," ",1) request.reply_code=string.atoi(code) else: request[header]=header_value request.push(html) request.done() def module_mtime(self): "returns the last modified date for a given module's source file" return os.stat(self.module.__file__)[8] def status (self): return producers.simple_producer ( '
  • Bobo Handler' + '' ) class input_collector: "gathers input for put and post requests" def __init__ (self, handler, request): self.handler = handler self.request = request self.data = StringIO() # make sure there's a content-length header self.cl = get_header (CONTENT_LENGTH, request.header) if not self.cl: request.error(411) return else: self.cl = string.atoi(self.cl) def collect_incoming_data (self, data): self.data.write(data) if self.data.tell() >= self.cl: self.data.seek(0) h=self.handler r=self.request # set the terminator back to the default self.request.channel.set_terminator ('\r\n\r\n') del self.handler del self.request h.continue_request(self.data,r)