#!/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
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([])
|