# -*- Mode: Python; tab-width: 4 -*- # This is a simple python server-side script handler. # A note about performance: This is really only suited for 'fast' # scripts: The script should generate its output quickly, since the # whole web server will stall otherwise. This doesn't mean you have # to write 'fast code' or anything, it simply means that you shouldn't # call any long-running code, [like say something that opens up an # internet connection, or a database query that will hold up the # server]. If you need this sort of feature, you can support it using # the asynchronous I/O 'api' that the rest of medusa is built on. [or # you could probably use threads] # Put your script into your web docs directory (like a cgi-bin # script), make sure it has the correct extension [see the overridable # script_handler.extension member below]. # # There's lots of things that can be done to tweak the restricted # execution model. Also, of course you could just use 'execfile' # instead (this is now the default, see class variable # script_handler.restricted) import rexec import regex import string import StringIO import sys import counter import default_handler import producers split_path = default_handler.split_path unquote = default_handler.unquote class script_handler: extension = 'mpy' restricted = 0 script_regex = regex.compile ( '.*/\([^/]+\.%s\)' % extension, regex.casefold ) def __init__ (self, filesystem): self.filesystem = filesystem self.hits = counter.counter() self.exceptions = counter.counter() def match (self, request): [path, params, query, fragment] = request.split_uri() if self.script_regex.match (path) == len(path): 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) if not self.filesystem.isfile (path): request.error (404) return else: self.hits.increment() request.script_filename = self.filesystem.translate (path) if request.command in ('put', 'post'): # look for a Content-Length header. cl = request.get_header ('content-length') length = int(cl) if not cl: request.error (411) else: collector (self, length, request) else: self.continue_request ( request, StringIO.StringIO() # empty stdin ) def continue_request (self, request, stdin): temp_files = stdin, StringIO.StringIO(), StringIO.StringIO() old_files = sys.stdin, sys.stdout, sys.stderr if self.restricted: r = rexec.RExec() try: sys.request = request sys.stdin, sys.stdout, sys.stderr = temp_files try: if self.restricted: r.s_execfile (request.script_filename) else: execfile (request.script_filename) request.reply_code = 200 except: request.reply_code = 500 self.exceptions.increment() finally: sys.stdin, sys.stdout, sys.stderr = old_files del sys.request i,o,e = temp_files if request.reply_code != 200: s = e.getvalue() else: s = o.getvalue() request['Content-Length'] = len(s) request.push (s) request.done() def status (self): return producer.simple_producer ( '
  • Server-Side Script Handler' + '' ) class persistent_script_handler: def __init__ (self): self.modules = {} self.hits = counter.counter() self.exceptions = counter.counter() def add_module (self, name, module): self.modules[name] = module def del_module (self, name): del self.modules[name] def match (self, request): [path, params, query, fragment] = request.split_uri() parts = string.split (path, '/') if (len(parts)>1) and self.modules.has_key (parts[1]): module = self.modules[parts[1]] request.module = module return 1 else: return 0 def handle_request (self, request): if request.command in ('put', 'post'): # look for a Content-Length header. cl = request.get_header ('content-length') length = int(cl) if not cl: request.error (411) else: collector (self, length, request) else: self.continue_request (request, StringIO.StringIO()) def continue_request (self, request, input_data): temp_files = input_data, StringIO.StringIO(), StringIO.StringIO() old_files = sys.stdin, sys.stdout, sys.stderr try: sys.stdin, sys.stdout, sys.stderr = temp_files # provide a default request['Content-Type'] = 'text/html' try: request.module.main (request) request.reply_code = 200 except: request.reply_code = 500 self.exceptions.increment() finally: sys.stdin, sys.stdout, sys.stderr = old_files i,o,e = temp_files if request.reply_code != 200: s = e.getvalue() else: s = o.getvalue() request['Content-Length'] = len(s) request.push (s) request.done() class collector: def __init__ (self, handler, length, request): self.handler = handler self.request = request self.request.collector = self self.request.channel.set_terminator (length) self.buffer = StringIO.StringIO() def collect_incoming_data (self, data): self.buffer.write (data) def found_terminator (self): self.buffer.seek(0) self.request.collector = None self.request.channel.set_terminator ('\r\n\r\n') self.handler.continue_request ( self.request, self.buffer )