#!python #------------------------------------------------------------------------ # Copyright (c) 1997 by Total Control Software # All Rights Reserved #------------------------------------------------------------------------ # # Module Name: lrwplib.py # # Description: Class LRWP handles the connection to the LRWP agent in # Xitami. This class can be used standalone or derived # from to override behavior. # # Creation Date: 11/11/97 8:36:21PM # # License: This is free software. You may use this software for any # purpose including modification/redistribution, so long as # this header remains intact and that you do not claim any # rights of ownership or authorship of this software. This # software has been tested, but no warranty is expressed or # implied. # #------------------------------------------------------------------------ import sys, socket, string import os, cgi from cStringIO import StringIO __version__ = '1.0' LENGTHSIZE = 9 LENGTHFMT = '%09d' #--------------------------------------------------------------------------- # Exception objects ConnectError = 'lrwp.ConnectError' ConnectionClosed = 'lrwp.ConnectionClosed' SocketError = 'lrwp.SocketError' #--------------------------------------------------------------------------- class Request: ''' Encapsulates the request/response IO objects and CGI-Environment. An instance of this class is returned ''' def __init__(self, lrwp): self.inp = lrwp.inp self.out = lrwp.out self.err = lrwp.out self.env = lrwp.env self.lrwp = lrwp def finish(self): self.lrwp.finish() def getFieldStorage(self): method = 'POST' if self.env.has_key('REQUEST_METHOD'): method = string.upper(self.env['REQUEST_METHOD']) if method == 'GET': return cgi.FieldStorage(environ=self.env, keep_blank_values=1) else: return cgi.FieldStorage(fp=self.inp, environ=self.env, keep_blank_values=1) #--------------------------------------------------------------------------- class LRWP: def __init__(self, name, host, port, vhost='', filter='', useStdio=0): ''' Construct an LRWP object. name The name or alias of this request handler. Requests matching http://host/name will be directed to this LRWP object. host Hostname or IP address to connect to. port Port number to connect on. vhost If this handler is to only be available to a specific virtual host, name it here. filter A space separated list of file extenstions that should be directed to this handler in filter mode. (Not yet supported.) ''' self.name = name self.host = host self.port = port self.vhost = vhost self.filter = filter self.useStdio = useStdio self.sock = None self.env = None self.inp = None self.out = None #---------------------------------------- def connect(self): ''' Establishes the connection to the web server, using the parameters given at construction. ''' try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((self.host, self.port)) # Changed to two item tuple with Python 2.0 self.sock.send("%s\xFF%s\xFF%s" % (self.name, self.vhost, self.filter) ) buf = self.sock.recv(1024) if buf != 'OK': raise ConnectError, buf except socket.error, val: raise SocketError, val #---------------------------------------- def acceptRequest(self): ''' Wait for, and accept a new request from the Web Server. Reads the name=value pairs that comprise the CGI environment, followed by the post data, if any. Constructs and returns a Request object. ''' if self.out: self.finish() try: # get the length of the environment data data = self.recvBlock(LENGTHSIZE) if not data: # server closed down raise ConnectionClosed length = string.atoi(data) # and then the environment data data = self.recvBlock(length) if not data: # server closed down raise ConnectionClosed data = string.split(data, '\000') self.env = {} for x in data: x = string.split(x, '=') if len(x) > 1: self.env[x[0]] = string.join(x[1:], '=') # now get the size of the POST data data = self.recvBlock(LENGTHSIZE) if not data: # server closed down raise ConnectionClosed length = string.atoi(data) # and the POST data... if length: data = self.recvBlock(length) if not data: # server closed down raise ConnectionClosed self.inp = StringIO(data) else: self.inp = StringIO() self.out = StringIO() # do the switcheroo on the sys IO objects, etc. if self.useStdio: self.saveStdio = sys.stdin, sys.stdout, sys.stderr, os.environ sys.stdin, sys.stdout, sys.stderr, os.environ = \ self.inp, self.out, self.out, self.env return Request(self) except socket.error, val: raise SocketError, val #---------------------------------------- def recvBlock(self, size): ''' Pull an exact number of bytes from the socket, taking into account the possibility of multiple packets... ''' numRead = 0 data = [] while numRead < size: buf = self.sock.recv(size - numRead); if not buf: return '' data.append(buf) numRead = numRead + len(buf) return string.join(data, '') #---------------------------------------- def finish(self): ''' Complete the request and send the output back to the webserver. ''' doc = self.out.getvalue() size = LENGTHFMT % (len(doc), ) try: self.sock.send(size) self.sock.send(doc) except socket.error, val: raise SocketError, val if self.useStdio: sys.stdin, sys.stdout, sys.stderr, os.environ = self.saveStdio self.env = None self.inp = None self.out = None #---------------------------------------- def close(self): ''' Close the LRWP connection to the web server. ''' self.sock.close() self.sock = None self.env = None self.inp = None self.out = None #--------------------------------------------------------------------------- def _test(): import os, time eol = '\r\n' appname = 'testapp1' vhost = '' host = 'localhost' port = 5081 if len(sys.argv) > 1: appname = sys.argv[1] if len(sys.argv) > 2: host = sys.argv[2] if len(sys.argv) > 3: port = string.atoi(sys.argv[3]) if len(sys.argv) > 4: vhost = sys.argv[4] lrwp = LRWP(appname, host, port, vhost) lrwp.connect() count = 0 while count < 5: # exit after servicing 5 requests req = lrwp.acceptRequest() doc = ['LRWP TestApp ('+appname+')\n\n'] count = count + 1 doc.append('

LRWP test app ('+appname+')

') doc.append('request count = %d
' % (count, )) if hasattr(os, 'getpid'): doc.append('pid = %s
' % (os.getpid(), )) doc.append('
post data: ' + req.inp.read() + '
') doc.append('


')
        keys = req.env.keys()
        keys.sort()
        for k in keys:
            doc.append('%-20s :  %s\n' % (k, req.env[k]))
        doc.append('\n


\n') doc.append('\n') req.out.write('Content-type: text/html' + eol) req.out.write(eol) req.out.write(string.join(doc, '')) req.finish() lrwp.close() if __name__ == '__main__': #import pdb #pdb.run('_test()') _test()