#!/usr/bin/env python """CGIPlusAppServer This WebKit app server is a WASD CGIplus server that accepts requests, hands them off to the Application and sends the request back over the connection. The fact that the app server stays resident is what makes it so much quicker than traditional CGI programming. Everything gets cached. CGIPlusAppServer takes the following command line arguments: start: start the AppServer (default argument) stop: stop the currently running Apperver ClassName.SettingName=value: change configuration settings When started, the app server records its pid in appserver.pid. """ import threading, Queue, select, socket, errno, traceback from marshal import dumps, loads from threading import Lock, Thread, Event from Common import * import AppServer as AppServerModule from AutoReloadingAppServer import AutoReloadingAppServer as AppServer from MiscUtils.Funcs import timestamp from WebUtils import Funcs debug = False server = None class CgiPlusAppServer(AppServer): ## Init ## def __init__(self, path=None): AppServer.__init__(self, path) self._requestID = 1 self.recordPID() self._wasd_running = None # temporaire from WebKit import Profiler Profiler.startTime = time.time() self.readyForRequests() def addInputHandler(self, handlerClass): self._handler = handlerClass def isPersistent(self): return False def recordPID(self): """Currently do nothing.""" return def initiateShutdown(self): self._wasd_running = False AppServer.initiateShutdown(self) def mainloop(self, timeout=1): import wasd wasd.init() stderr_ini = sys.stderr sys.stderr = StringIO() self._wasd_running = True environ_ini = os.environ while 1: if not self._running or not self._wasd_running: return # init environment cgi variables os.environ = environ_ini.copy() wasd.init_environ() print >>sys.__stdout__, "Script-Control: X-stream-mode" self._requestID += 1 self._app._sessions.cleanStaleSessions() self.handler = handler = self._handler(self) handler.activate(self._requestID) handler.handleRequest() self.restartIfNecessary() self.handler = None sys.__stdout__.flush() if not self._running or not self._wasd_running: return # when we want to exit don't send the eof, so # WASD don't try to send the next request to the server wasd.cgi_eof() sys.stderr.close() # block until next request wasd.cgi_info("") sys.stderr = StringIO() def shutDown(self): self._running = 0 print "CgiPlusAppServer: Shutting Down" AppServer.shutDown(self) class Handler: def __init__(self, server): self._server = server def activate(self, requestID): """Activates the handler for processing the request. Number is the number of the request, mostly used to identify verbose output. Each request should be given a unique, incremental number. """ self._requestID = requestID def close(self): pass def handleRequest(self): pass def receiveDict(self): """Utility function to receive a marshalled dictionary.""" pass from WebKit.ASStreamOut import ASStreamOut class CPASStreamOut(ASStreamOut): """Response stream for CgiPLusAppServer. The `CPASASStreamOut` class streams to a given file, so that when `flush` is called and the buffer is ready to be written, it sends the data from the buffer out on the file. This is the response stream used for requests generated by CgiPlusAppServer. CP stands for CgiPlusAppServer """ def __init__(self, file): ASStreamOut.__init__(self) self._file = file def flush(self): result = ASStreamOut.flush(self) if result: # a true return value means we can send reslen = len(self._buffer) self._file.write(self._buffer) self._file.flush() sent = reslen self.pop(sent) # Set to False in DebugAppServer so Python debuggers can trap exceptions doesRunHandleExceptions = True class RestartAppServerError(Exception): """Raised by DebugAppServer when needed.""" pass def run(workDir=None): global server from WebKit.CgiPlusServer import CgiPlusAppServerHandler runAgain = True while runAgain: # looping in support of RestartAppServerError try: try: runAgain = False server = None server = CgiPlusAppServer(workDir) server.addInputHandler(CgiPlusAppServerHandler) try: server.mainloop() except KeyboardInterrupt, e: server.shutDown() except RestartAppServerError: print print "Restarting app server:" sys.stdout.flush() runAgain = True except Exception, e: if not doesRunHandleExceptions: raise if not isinstance(e, SystemExit): import traceback traceback.print_exc(file=sys.stderr) print print "Exiting AppServer" if server: if server._running: server.initiateShutdown() # if we're here as a result of exit() being called, # exit with that return code. if isinstance(e,SystemExit): sys.exit(e) finally: AppServerModule.globalAppServer = None sys.exit() def shutDown(arg1, arg2): global server print "Shutdown Called", asclocaltime() if server: server.initiateShutdown() else: print 'WARNING: No server reference to shutdown.' import signal signal.signal(signal.SIGINT, shutDown) signal.signal(signal.SIGTERM, shutDown) usage = re.search('\n.* arguments:\n\n(.*\n)*?\n', __doc__).group(0) import re settingRE = re.compile(r'^--([a-zA-Z][a-zA-Z0-9]*\.[a-zA-Z][a-zA-Z0-9]*)=') from MiscUtils import Configurable def main(args): function = run workDir = None sys.stdout = StringIO() for i in args[:]: if settingRE.match(i): match = settingRE.match(i) name = match.group(1) value = i[match.end():] Configurable.addCommandLineSetting(name, value) elif i == "stop": function = AppServerModule.stop elif i == "start": pass elif i[:8] == "workdir=": workDir = i[8:] else: print usage function(workDir=workDir) main([])