home *** CD-ROM | disk | FTP | other *** search
/ Australian Personal Computer 1999 May / APC452.ISO / netkit / xitami / xitami.exe / LRWPLIB.PY < prev    next >
Encoding:
Python Source  |  1998-08-15  |  8.8 KB  |  274 lines

  1. #!/bin/env python
  2. #------------------------------------------------------------------------
  3. #               Copyright (c) 1997 by Total Control Software
  4. #                         All Rights Reserved
  5. #------------------------------------------------------------------------
  6. #
  7. # Module Name:  lrwplib.py
  8. #
  9. # Description:  Class LRWP handles the connection to the LRWP agent in
  10. #               Xitami.  This class can be used standalone or derived
  11. #               from to override behavior.
  12. #
  13. # Creation Date:    11/11/97 8:36:21PM
  14. #
  15. # License:      This is free software.  You may use this software for any
  16. #               purpose including modification/redistribution, so long as
  17. #               this header remains intact and that you do not claim any
  18. #               rights of ownership or authorship of this software.  This
  19. #               software has been tested, but no warranty is expressed or
  20. #               implied.
  21. #
  22. #------------------------------------------------------------------------
  23.  
  24. import  sys, socket, string
  25. import  os, cgi
  26. from    cStringIO import StringIO
  27.  
  28.  
  29. __version__ = '1.0'
  30.  
  31. LENGTHSIZE  = 9
  32. LENGTHFMT   = '%09d'
  33.  
  34. #---------------------------------------------------------------------------
  35. # Exception objects
  36.  
  37. ConnectError        = 'lrwp.ConnectError'
  38. ConnectionClosed    = 'lrwp.ConnectionClosed'
  39. SocketError         = 'lrwp.SocketError'
  40.  
  41. #---------------------------------------------------------------------------
  42.  
  43. class Request:
  44.     '''
  45.     Encapsulates the request/response IO objects and CGI-Environment.
  46.     An instance of this class is returned
  47.     '''
  48.     def __init__(self, lrwp):
  49.         self.inp = lrwp.inp
  50.         self.out = lrwp.out
  51.         self.err = lrwp.out
  52.         self.env = lrwp.env
  53.         self.lrwp = lrwp
  54.  
  55.     def finish(self):
  56.         self.lrwp.finish()
  57.  
  58.     def getFieldStorage(self):
  59.         method = 'POST'
  60.         if self.env.has_key('REQUEST_METHOD'):
  61.             method = string.upper(self.env['REQUEST_METHOD'])
  62.         if method == 'GET':
  63.             return cgi.FieldStorage(environ=self.env, keep_blank_values=1)
  64.         else:
  65.             return cgi.FieldStorage(fp=self.inp, environ=self.env, keep_blank_values=1)
  66.  
  67.  
  68. #---------------------------------------------------------------------------
  69.  
  70. class LRWP:
  71.     def __init__(self, name, host, port, vhost='', filter='', useStdio=0):
  72.         '''
  73.         Construct an LRWP object.
  74.             name        The name or alias of this request handler.  Requests
  75.                         matching http://host/name will be directed to this
  76.                         LRWP object.
  77.             host        Hostname or IP address to connect to.
  78.             port        Port number to connect on.
  79.             vhost       If this handler is to only be available to a specific
  80.                         virtual host, name it here.
  81.             filter      A space separated list of file extenstions that should
  82.                         be directed to this handler in filter mode.  (Not yet
  83.                         supported.)
  84.         '''
  85.         self.name = name
  86.         self.host = host
  87.         self.port = port
  88.         self.vhost = vhost
  89.         self.filter = filter
  90.         self.useStdio = useStdio
  91.         self.sock = None
  92.         self.env = None
  93.         self.inp = None
  94.         self.out = None
  95.  
  96.     #----------------------------------------
  97.     def connect(self):
  98.         '''
  99.         Establishes the connection to the web server, using the parameters given
  100.         at construction.
  101.         '''
  102.         try:
  103.             self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  104.             self.sock.connect(self.host, self.port)
  105.             self.sock.send("%s\xFF%s\xFF%s" % (self.name, self.vhost, self.filter) )
  106.             buf = self.sock.recv(1024)
  107.             if buf != 'OK':
  108.                 raise ConnectError, buf
  109.         except socket.error, val:
  110.             raise SocketError, val
  111.  
  112.     #----------------------------------------
  113.     def acceptRequest(self):
  114.         '''
  115.         Wait for, and accept a new request from the Web Server.  Reads the
  116.         name=value pairs that comprise the CGI environment, followed by the
  117.         post data, if any.  Constructs and returns a Request object.
  118.         '''
  119.         if self.out:
  120.             self.finish()
  121.         try:
  122.             # get the length of the environment data
  123.             data = self.recvBlock(LENGTHSIZE)
  124.             if not data:        # server closed down
  125.                 raise ConnectionClosed
  126.             length = string.atoi(data)
  127.  
  128.             # and then the environment data
  129.             data = self.recvBlock(length)
  130.             if not data:        # server closed down
  131.                 raise ConnectionClosed
  132.             data = string.split(data, '\000')
  133.             self.env = {}
  134.             for x in data:
  135.                 x = string.split(x, '=')
  136.                 if len(x) > 1:
  137.                     self.env[x[0]] = string.join(x[1:], '=')
  138.  
  139.             # now get the size of the POST data
  140.             data = self.recvBlock(LENGTHSIZE)
  141.             if not data:        # server closed down
  142.                 raise ConnectionClosed
  143.             length = string.atoi(data)
  144.  
  145.             # and the POST data...
  146.             if length:
  147.                 data = self.recvBlock(length)
  148.                 if not data:        # server closed down
  149.                     raise ConnectionClosed
  150.                 self.inp = StringIO(data)
  151.             else:
  152.                 self.inp = StringIO()
  153.  
  154.             self.out = StringIO()
  155.  
  156.             # do the switcheroo on the sys IO objects, etc.
  157.             if self.useStdio:
  158.                 self.saveStdio = sys.stdin, sys.stdout, sys.stderr, os.environ
  159.                 sys.stdin, sys.stdout, sys.stderr, os.environ = \
  160.                     self.inp, self.out, self.out, self.env
  161.  
  162.             return Request(self)
  163.  
  164.         except socket.error, val:
  165.             raise SocketError, val
  166.  
  167.  
  168.     #----------------------------------------
  169.     def recvBlock(self, size):
  170.         '''
  171.         Pull an exact number of bytes from the socket, taking into
  172.         account the possibility of multiple packets...
  173.         '''
  174.         numRead = 0
  175.         data = []
  176.         while numRead < size:
  177.             buf = self.sock.recv(size - numRead);
  178.             if not buf:
  179.                 return ''
  180.             data.append(buf)
  181.             numRead = numRead + len(buf)
  182.  
  183.         return string.join(data, '')
  184.  
  185.     #----------------------------------------
  186.     def finish(self):
  187.         '''
  188.         Complete the request and send the output back to the webserver.
  189.         '''
  190.         doc = self.out.getvalue()
  191.         size = LENGTHFMT % (len(doc), )
  192.         try:
  193.             self.sock.send(size)
  194.             self.sock.send(doc)
  195.         except socket.error, val:
  196.             raise SocketError, val
  197.  
  198.         if self.useStdio:
  199.             sys.stdin, sys.stdout, sys.stderr, os.environ = self.saveStdio
  200.  
  201.         self.env = None
  202.         self.inp = None
  203.         self.out = None
  204.  
  205.     #----------------------------------------
  206.     def close(self):
  207.         '''
  208.         Close the LRWP connection to the web server.
  209.         '''
  210.         self.sock.close()
  211.         self.sock = None
  212.         self.env = None
  213.         self.inp = None
  214.         self.out = None
  215.  
  216. #---------------------------------------------------------------------------
  217.  
  218.  
  219. def _test():
  220.     import os, time
  221.  
  222.     eol = '\r\n'
  223.     appname = 'testapp1'
  224.     vhost = ''
  225.     host = 'localhost'
  226.     port = 5081
  227.     if len(sys.argv) > 1:
  228.         appname = sys.argv[1]
  229.     if len(sys.argv) > 2:
  230.         host = sys.argv[2]
  231.     if len(sys.argv) > 3:
  232.         port = string.atoi(sys.argv[3])
  233.     if len(sys.argv) > 4:
  234.         vhost = sys.argv[4]
  235.  
  236.     lrwp = LRWP(appname, host, port, vhost)
  237.     lrwp.connect()
  238.  
  239.     count = 0
  240.     while count < 5:        # exit after servicing 5 requests
  241.         req = lrwp.acceptRequest()
  242.  
  243.         doc = ['<HTML><HEAD><TITLE>LRWP TestApp ('+appname+')</TITLE></HEAD>\n<BODY>\n']
  244.         count = count + 1
  245.         doc.append('<H2>LRWP test app ('+appname+')</H2><P>')
  246.         doc.append('<b>request count</b> = %d<br>' % (count, ))
  247.         if hasattr(os, 'getpid'):
  248.             doc.append('<b>pid</b> = %s<br>' % (os.getpid(), ))
  249.         doc.append('<br><b>post data:</b> ' + req.inp.read() + '<br>')
  250.  
  251.         doc.append('<P><HR><P><pre>')
  252.         keys = req.env.keys()
  253.         keys.sort()
  254.         for k in keys:
  255.             doc.append('<b>%-20s :</b>  %s\n' % (k, req.env[k]))
  256.         doc.append('\n</pre><P><HR>\n')
  257.         doc.append('</BODY></HTML>\n')
  258.  
  259.  
  260.         req.out.write('Content-type: text/html' + eol)
  261.         req.out.write(eol)
  262.         req.out.write(string.join(doc, ''))
  263.  
  264.         req.finish()
  265.  
  266.     lrwp.close()
  267.  
  268.  
  269. if __name__ == '__main__':
  270.     #import pdb
  271.     #pdb.run('_test()')
  272.     _test()
  273.  
  274.