Here's a set patches to Webware (CVS as of today),
which I hope will be useful to others.
The changes are:
* WebKit/Session.py:
        Improve security by making the session identifier
        harder to guess.
* WebKit/SessionFileStore.py:
        Avoid concurrent session file update woes, which may cause
        pickle.load exceptions, by writing to a temporary
        file, then renaming.
* WebKit/UnknownFileTypeServlet.py:
        Handle HEAD requests a little better.
* WebKit/Application.py:
        New method: getDbConnection(), which returns
        a (pooled) database connection.
        Requires 3 new Application.config settings, e.g.:
                'DbModule': 'PgSQL',    # your fav DB-API v2.0 module
                'DbConnect':'::mydb:me', # DB connection string
                'DbConnections': 5,      # concurrent connections
        Connections in the pool are closed at application
        shutdown, in the interest of database hygiene.
        I prefer this method over Cans, because it's simpler,
        it centralizes the DB stuff in one place, and it's the sort
        of natural functionality WebKit should support out of the box.
* MiscUtils/DBPool.py:
        Added shutDown method, as described above.
* WebKit/HTTPServlet.py:
        Attempts to mirror the Webware site I'm working on
        using "wget -m" caused a KeyError in HTTPServlet.respond(),
        because WebKit doesn't support the HTTP HEAD method
        (a mandatory part of the protocol).
        Changed to return a "501 Not Implemented" status
        if the subclass doesn't define the method.
        I also added a default respondToHead method, which is
        correct but inefficient.

Cheers, - Ken Lalonde, Torus Inc.


diff -c /tmp/k/Webware/WebKit/Session.py ./Session.py
*** /tmp/k/Webware/WebKit/Session.py    Sun Aug 12 20:01:07 2001
--- ./Session.py        Fri Oct 12 15:56:05 2001
***************
*** 1,5 ****
  from Common import *
! import whrandom
  from time import localtime, time
  from CanContainer import *
  
--- 1,5 ----
  from Common import *
! import os, random, md5
  from time import localtime, time
  from CanContainer import *
  
***************
*** 48,54 ****
  
                attempts = 0
                while attempts<10000:
!                       self._identifier = string.join(map(lambda x: '%02d' % x, 
localtime(time())[:6]), '') + str(whrandom.randint(10000, 99999))
                        if not trans.application().hasSession(self._identifier):
                                break
                        attempts = attempts + 1
--- 48,58 ----
  
                attempts = 0
                while attempts<10000:
!                       # id(self) should guarantee uniqueness.
!                       # Use md5() to make session keys hard to guess.
!                       # We assume a reasonably good random seed.
!                       r = [time(), random.random(), id(self), os.times()]
!                       self._identifier = md5.new(str(r)).hexdigest()
                        if not trans.application().hasSession(self._identifier):
                                break
                        attempts = attempts + 1
diff -c /tmp/k/Webware/WebKit/SessionFileStore.py ./SessionFileStore.py
*** /tmp/k/Webware/WebKit/SessionFileStore.py   Wed Mar 14 22:34:35 2001
--- ./SessionFileStore.py       Tue Sep 25 17:12:04 2001
***************
*** 52,59 ****
                if debug:
                        print '>> setitem(%s,%s)' % (key, item)
                filename = self.filenameForKey(key)
!               file = open(filename, 'w')
                self.encoder()(item, file)
  
        def __delitem__(self, key):
                filename = self.filenameForKey(key)
--- 52,62 ----
                if debug:
                        print '>> setitem(%s,%s)' % (key, item)
                filename = self.filenameForKey(key)
!               tmp = os.tempnam(os.path.dirname(filename), 'tmp')
!               file = open(tmp, 'w')
                self.encoder()(item, file)
+               file.close()
+               os.rename(tmp, filename)
  
        def __delitem__(self, key):
                filename = self.filenameForKey(key)
diff -c /tmp/k/Webware/WebKit/UnknownFileTypeServlet.py ./UnknownFileTypeServlet.py
*** /tmp/k/Webware/WebKit/UnknownFileTypeServlet.py     Sun Apr 29 20:37:01 2001
--- ./UnknownFileTypeServlet.py Thu Oct 18 17:43:08 2001
***************
*** 1,5 ****
  from ServletFactory import ServletFactory
! import os, mimetypes
  
  debug = 0
  
--- 1,5 ----
  from ServletFactory import ServletFactory
! import os, mimetypes, time
  
  debug = 0
  
***************
*** 51,56 ****
--- 51,59 ----
                method = getattr(self, technique)
                method(trans)
  
+       def respondToHead(self, trans):
+               self.respondToGet(trans)
+ 
        def respondToPost(self, trans):
                '''
                Invokes self.respondToGet().
***************
*** 84,89 ****
--- 87,99 ----
                filename = trans.request().serverSidePath()
                filesize = os.path.getsize(filename)
  
+               head = trans.request().method().upper()[0] == 'H'
+               if head:
+                       response.setHeader('Content-Length', str(filesize))
+                       m = os.path.getmtime(filename)
+                       response.setHeader('Last-Modified',
+                               time.strftime('%a, %d %b %Y %H:%M:%S GMT',
+                                       time.gmtime(m)))
                if debug:
                        print '>> UnknownFileType.serveContent()'
                        print '>> filename =', filename
***************
*** 101,106 ****
--- 111,118 ----
                        response.setHeader('Content-type', self._mimeType)
                        if self._mimeEncoding:
                                response.setHeader('Content-encoding', 
self._mimeEncoding)
+                       if head:
+                               return
                        response.write(data)
  
                else:
***************
*** 125,132 ****
--- 137,146 ----
                                self._mimeEncoding = mimeEncoding
                                self._mtime = os.path.getmtime(filename)
                                self._serverSideFilename = filename
+                               if head: return
                                response.write(self._content)
                        else:  ##too big
+                               if head: return
                                f = open(filename, "rb")
                                sent = 0
                                while sent < filesize:
diff -c /tmp/k/Webware/WebKit/Application.py ./Application.py
*** /tmp/k/Webware/WebKit/Application.py        Tue Oct 23 17:49:44 2001
--- ./Application.py    Tue Oct 23 17:49:00 2001
***************
*** 101,106 ****
--- 101,107 ----
                self._serverSideInfoCacheByPath = {}
                self._cacheDictLock = Lock()
                self._instanceCacheSize = self._server.setting('MaxServerThreads')
+               self._dbPool = None
                self._canDirs = []
  
                self.initializeCans()
***************
*** 237,242 ****
--- 238,245 ----
                del self._factoryList
                del self._server
                del self._servletCacheByPath
+               if self._dbPool:
+                       self._dbPool.shutDown()
                print "Application has been succesfully shutdown."
  
  
***************
*** 1186,1191 ****
--- 1189,1206 ----
                return ssPath, urlPath, extraURLPath
  
  
+       # Pooled db connection support.
+       def getDbConnection(self):
+               if not self._dbPool:
+                       from MiscUtils.DBPool import DBPool
+                       self._dbPool = DBPool(
+                               # our DB-API v2.0 module:
+                               __import__(self.setting('DbModule')),
+                               # number of concurrent connections:
+                               self.setting('DbConnections'),
+                               # DB module connection string:
+                               self.setting('DbConnect'))
+               return self._dbPool.getConnection()     # may block
  
  
        ## Deprecated ##
diff -c /tmp/k/Webware/MiscUtils/DBPool.py ../MiscUtils/DBPool.py
*** /tmp/k/Webware/MiscUtils/DBPool.py  Tue Oct 23 17:51:42 2001
--- ../MiscUtils/DBPool.py      Tue Oct 23 17:51:36 2001
***************
*** 83,93 ****
--- 83,101 ----
                        self.getConnection = self._threadsafe_getConnection
                        self.returnConnection = self._threadsafe_returnConnection
  
+               self._cons = []
                # @@ 2000-12-04 ce: Should we really make all the connections now?
                # Couldn't we do this on demand?
                for i in range(maxConnections):
                        con = apply(dbModule.connect, args, kwargs)
                        self.addConnection(con)
+                       self._cons.append(con)
+       
+       def shutDown(self):
+               """ Attempt to cleanly shut down all connections """
+               try:    [c.close() for c in self._cons]
+               except: pass
+ 
  
  
        # threadsafe/unthreadsafe refers to the database _module_, not THIS class..
diff -c /tmp/k/Webware/WebKit/HTTPServlet.py ./HTTPServlet.py
*** /tmp/k/Webware/WebKit/HTTPServlet.py        Fri Jan 12 22:12:43 2001
--- ./HTTPServlet.py    Thu Oct 18 17:19:26 2001
***************
*** 5,12 ****
  class HTTPServlet(Servlet):
        '''
        HTTPServlet implements the respond() method to invoke methods such as 
respondToGet() and respondToPut() depending on the type of HTTP request.
!       Current supported request are GET, POST, PUT, DELETE, OPTIONS and TRACE.
!       The dictionary _methodForRequestType contains the information about the types 
of requests and their corresponding methods.
  
        Note that HTTPServlet inherits awake() and respond() methods from Servlet and 
that subclasses may make use of these.
  
--- 5,12 ----
  class HTTPServlet(Servlet):
        '''
        HTTPServlet implements the respond() method to invoke methods such as 
respondToGet() and respondToPut() depending on the type of HTTP request.
!       Subclasses implement HTTP method FOO in the Python method respondToFoo.
!       Unsupported methods return a "501 Not Implemented" status.
  
        Note that HTTPServlet inherits awake() and respond() methods from Servlet and 
that subclasses may make use of these.
  
***************
*** 16,56 ****
                * Document methods (take hints from Java HTTPServlet documentation)
        '''
  
-       ## Init ##
- 
-       def __init__(self):
-               Servlet.__init__(self)
-               self._methodForRequestType = {
-                       'GET':     self.__class__.respondToGet,
-                       'POST':    self.__class__.respondToPost,
-                       'PUT':     self.__class__.respondToPut,
-                       'DELETE':  self.__class__.respondToDelete,
-                       'OPTIONS': self.__class__.respondToOptions,
-                       'TRACE':   self.__class__.respondToTrace,
-               }
- 
- 
        ## Transactions ##
  
        def respond(self, trans):
                ''' Invokes the appropriate respondToSomething() method depending on 
the type of request (e.g., GET, POST, PUT, ...). '''
!               method = self._methodForRequestType[trans.request().method()]
!               method(self, trans)
! 
!       def respondToGet(self, trans):
!               raise SubclassResponsibilityError
! 
!       def respondToPost(self, trans):
!               raise SubclassResponsibilityError
! 
!       def respondToPut(self, trans):
!               raise SubclassResponsibilityError
! 
!       def respondToDelete(self, trans):
!               raise SubclassResponsibilityError
! 
!       def respondToOptions(self, trans):
!               raise SubclassResponsibilityError
! 
!       def respondToTrace(self, trans):
!               raise SubclassResponsibilityError
--- 16,37 ----
                * Document methods (take hints from Java HTTPServlet documentation)
        '''
  
        ## Transactions ##
  
        def respond(self, trans):
                ''' Invokes the appropriate respondToSomething() method depending on 
the type of request (e.g., GET, POST, PUT, ...). '''
!               attr = 'respondTo' + trans.request().method().capitalize()
!               getattr(self, attr, self.notImplemented)(trans)
!       
!       def notImplemented(self, trans):
!               trans.response().setHeader('Status', '501 Not Implemented')
!       
!       def respondToHead(self, trans):
!               ''' A correct but inefficient implementation.
!                   Should at least provide Last-Modified and Content-Length.
!               '''
!               res = trans.response()
!               w = res.write
!               res.write = lambda *args: None
!               self.respondToGet(trans)
!               res.write = w


_______________________________________________
Webware-discuss mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/webware-discuss

Reply via email to