DEVELOPERS GUIDE

Module:pyfileserver
Author: Ho Chun Wei, fuzzybr80(at)gmail.com
Project:PyFileServer, http://pyfilesync.berlios.de/
Copyright: Lesser GNU Public License, see LICENSE file attached with package

This section attempts to give a brief introduction to the PyFileServer application package.

WSGI Application

PyFileServer is a WSGI application.

WSGI <http://www.python.org/peps/pep-0333.html> stands for Web Server Gateway Interface, a proposed standard interface between web servers and Python web applications or frameworks, to promote web application portability across a variety of web servers. If you are unfamiliar with WSGI, do take a moment to read the PEP. Most WSGI application consists middleware which serve as pre-filters and post-processors, and the actual application.

PyFileServer:

Request -> PyFileApp (container)
                 |
                 +-> ErrorPrinter (middleware)
                          |
                    RequestResolver (middleware)
                          |
                  HTTPAuthenticator (middleware)
                          |
                    RequestServer (application)

In addition, PyFileServer comes with an existing WSGI webserver so that the application can be run as a standalone program.

The PyFileServer application consists of the following modules:

Bundled Web Server:
      ext_wsgiutils_server 

Application objects: 
      pyfileserver.mainappwrapper
         + class PyFileApp
   
      pyfileserver.processrequesterrorhandler    
         + class ErrorPrinter
         + Exception HTTPRequestException
   
      pyfileserver.requestresolver
         + class RequestResolver
      
      pyfileserver.httpauthentication
         + class HTTPAuthenticator
         + class SimpleDomainController    
      
      pyfileserver.pyfiledomaincontroller
         + class PyFileServerDomainController    
      
      pyfileserver.extrequestserver    
         + class RequestServer
   
      pyfileserver.propertylibrary    
         + class LockManager
         + class PropertyManager
   
      pyfileserver.etagprovider    
         + func object getETag

Miscellaneous libraries:
      pyfileserver.websupportfuncs    
      pyfileserver.loadconfig_primitive    
      pyfileserver.httpdatehelper    

Each of these modules are documented below.


Running PyFileServer

PyFileServer comes bundled with a simple wsgi webserver.

Running as standalone server

To run as a standalone server using the bundled ext_wsgiutils_server.py:

usage: python ext_wsgiutils_server.py [options] [config-file]

config-file:
  The configuration file for PyFileServer. if omitted, the application
  will look for a file named 'PyFileServer.conf' in the current directory

options:
  --port=PORT  Port to serve on (default: 8080)
  --host=HOST  Host to serve from (default: localhost, which is only
               accessible from the local computer; use 0.0.0.0 to make your
               application public)
  -h, --help   show this help message and exit

Running using other web servers

To run it with other WSGI web servers, you can:

from pyfileserver.mainappwrapper import PyFileApp
publish_app = PyFileApp('PyFileServer.conf')   
# construct the application with configuration file 
# if configuration file is omitted, the application
# will look for a file named 'PyFileServer.conf'
# in the current directory

where publish_app is the WSGI application to be run, it will be called with publish_app(environ, start_response) for each incoming request, as described in WSGI <http://www.python.org/peps/pep-0333.html>

Note: if you are using the paster development server (from Paste <http://pythonpaste.org>), you can copy ext_wsgi_server.py to <Paste-installation>/paste/servers and use this server to run the application by specifying server='ext_wsgiutils' in the server.conf or appropriate paste configuration.

About ext_wsgiutils_server

ext_wsgiutils_server.py is an extension of the wsgiutils server in Paste. It supports passing all of the HTTP and WebDAV (rfc 2518) methods.

It includes code from the following sources: wsgiServer.py from wsgiKit <http://www.owlfish.com/software/wsgiutils/> under PSF license, wsgiutils_server.py from Paste <http://pythonpaste.org> under PSF license, flexible handler method <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/307618> under public domain.


mainappwrapper

Module:pyfileserver.mainappwrapper
Author:Ho Chun Wei, fuzzybr80(at)gmail.com
Project:PyFileServer, http://pyfilesync.berlios.de/
Copyright:Lesser GNU Public License, see LICENSE file attached with package

See Running PyFileServer in ext_wsgiutils_server.py


processrequesterrorhandler

Module:pyfileserver.processrequesterrorhandler
Author:Ho Chun Wei, fuzzybr80(at)gmail.com
Project:PyFileServer, http://pyfilesync.berlios.de/
Copyright:Lesser GNU Public License, see LICENSE file attached with package

WSGI Middleware to catch application thrown HTTPRequestExceptions and return proper responses

Usage:

from pyfileserver.processrequesterrorhandler import ErrorPrinter
WSGIApp = ErrorPrinter(ProtectedWSGIApp, server_descriptor, catchall)

where:
   ProtectedWSGIApp is the application throwing HTTPRequestExceptions, 

   server_descriptor is an optional html string to be included as the 
   footer of any html response sent  

   catchall is an optional boolean. if True, ErrorPrinter will catch all
   other exceptions and print a trace to sys.stderr stream before sending
   a 500 Internal Server Error response (default = False)


Within ProtectedWSGIApp:

   from pyfileserver import processrequesterrorhandler
   from pyfileserver.processrequesterrorhandler import HTTPRequestException
   ...
   ...
   raise HTTPRequestException(404)
      or
   raise HTTPRequestException(processrequesterrorhandler.HTTP_BAD_REQUEST)
   #escape the existing application and return the 404 Bad Request immediately

Occasionally it may be useful for an internal ProtectedWSGIApp method to catch the HTTPRequestException (for compiling into a multi-status, for example). The response code of the error can be returned as:

from pyfileserver import processrequesterrorhandler
from pyfileserver.processrequesterrorhandler import HTTPRequestException

try:
   ...
   raise HTTPRequestException(processrequesterrorhandler.HTTP_BAD_REQUEST)
   ...
except HTTPRequestException, e:
   numberCode = processrequesterrorhandler.getErrorCodeFromException(e)
   textCode = processrequesterrorhandler.interpretErrorException(e)

Interface

Classes:

  • 'ErrorPrinter': WSGI Middleware to catch HTTPRequestExceptions and return proper responses

Exception(s):

  • 'HTTPRequestException': Raised with error code integer (1xx-5xx) within protected application to be caught by ErrorPrinter

Function(s):

  • 'interpretErrorException(e)': Returns response code string for HTTPRequestException e.
  • 'getErrorCodeFromException(e)': Returns the response code number (1xx-5xx) for HTTPRequestException e

Constants:

HTTP_CONTINUE = 100
HTTP_SWITCHING_PROTOCOLS = 101
HTTP_PROCESSING = 102
HTTP_OK = 200
HTTP_CREATED = 201
HTTP_ACCEPTED = 202
HTTP_NON_AUTHORITATIVE_INFO = 203
HTTP_NO_CONTENT = 204
HTTP_RESET_CONTENT = 205
HTTP_PARTIAL_CONTENT = 206
HTTP_MULTI_STATUS = 207
HTTP_IM_USED = 226
HTTP_MULTIPLE_CHOICES = 300
HTTP_MOVED = 301
HTTP_FOUND = 302
HTTP_SEE_OTHER = 303
HTTP_NOT_MODIFIED = 304
HTTP_USE_PROXY = 305
HTTP_TEMP_REDIRECT = 307
HTTP_BAD_REQUEST = 400
HTTP_PAYMENT_REQUIRED = 402
HTTP_FORBIDDEN = 403
HTTP_NOT_FOUND = 404
HTTP_METHOD_NOT_ALLOWED = 405
HTTP_NOT_ACCEPTABLE = 406
HTTP_PROXY_AUTH_REQUIRED = 407
HTTP_REQUEST_TIMEOUT = 408
HTTP_CONFLICT = 409
HTTP_GONE = 410
HTTP_LENGTH_REQUIRED = 411
HTTP_PRECONDITION_FAILED = 412
HTTP_REQUEST_ENTITY_TOO_LARGE = 413
HTTP_REQUEST_URI_TOO_LONG = 414
HTTP_MEDIATYPE_NOT_SUPPORTED = 415
HTTP_RANGE_NOT_SATISFIABLE = 416
HTTP_EXPECTATION_FAILED = 417
HTTP_UNPROCESSABLE_ENTITY = 422
HTTP_LOCKED = 423
HTTP_FAILED_DEPENDENCY = 424
HTTP_UPGRADE_REQUIRED = 426
HTTP_INTERNAL_ERROR = 500
HTTP_NOT_IMPLEMENTED = 501
HTTP_BAD_GATEWAY = 502
HTTP_SERVICE_UNAVAILABLE = 503
HTTP_GATEWAY_TIMEOUT = 504
HTTP_VERSION_NOT_SUPPORTED = 505
HTTP_INSUFFICIENT_STORAGE = 507
HTTP_NOT_EXTENDED = 510

requestresolver

Module:pyfileserver.requestresolver
Author:Ho Chun Wei, fuzzybr80(at)gmail.com
Project:PyFileServer, http://pyfilesync.berlios.de/
Copyright:Lesser GNU Public License, see LICENSE file attached with package

PyFileServer file sharing

PyFileServer allows the user to specify in PyFileServer.conf a number of realms, and a number of users for each realm.

Realms

Each realm corresponds to a filestructure on disk to be stored, for example:

addrealm('pubshare','/home/public/share') 

would allow the users to access using WebDAV the directory/file structure at /home/public/share from the url http://<servername:port>/<approot>/pubshare

The realm name is set as '/pubshare'

e.g. /home/public/share/PyFileServer/LICENSE becomes accessible as http://<servername:port>/<approot>/pubshare/PyFileServer/LICENSE

Users

A number of username/password pairs can be set for each realm:

adduser('pubshare', 'username', 'password', 'description/unused')

would add a username/password pair to realm /pubshare.

Note: if developers wish to maintain a separate users database, you can write your own domain controller for the HTTPAuthenticator. See httpauthentication.py and pyfiledomaincontroller.py for more details.

Request Resolver

This module is specific to the PyFileServer application

WSGI Middleware for Resolving Realm and Paths for the PyFileServer application.

Usage:

from pyfileserver.requestresolver import RequestResolver
WSGIApp = RequestResolver(InternalWSGIApp)

The RequestResolver resolves the requested URL to the following values placed in the environ dictionary. First it resolves the corresponding realm:

url: http://<servername:port>/<approot>/pubshare/PyFileServer/LICENSE
environ['pyfileserver.mappedrealm'] = /pubshare

Based on the configuration given, the resource abstraction layer for the realm is determined. if no configured abstraction layer is found, the default abstraction layer fileabstractionlayer.FilesystemAbstractionLayer() is used:

environ['pyfileserver.resourceAL'] = fileabstractionlayer.MyOwnFilesystemAbstractionLayer()

The path identifiers for the requested url are then resolved using the resource abstraction layer:

environ['pyfileserver.mappedpath'] = /home/public/share/PyFileServer/LICENSE 
environ['pyfileserver.mappedURI'] = /pubshare/PyFileServer/LICENSE

in this case, FilesystemAbstractionLayer resolves any relative paths to its canonical absolute path

The RequestResolver also resolves any value in the Destination request header, if present, to:

Destination: http://<servername:port>/<approot>/pubshare/PyFileServer/LICENSE-dest
environ['pyfileserver.destrealm'] = /pubshare
environ['pyfileserver.destpath'] = /home/public/share/PyFileServer/LICENSE-dest 
environ['pyfileserver.destURI'] = /pubshare/PyFileServer/LICENSE
environ['pyfileserver.destresourceAL'] = fileabstractionlayer.MyOwnFilesystemAbstractionLayer()

Interface

classes:

RequestResolver: Request resolver for PyFileServer

httpauthentication

Module:pyfileserver.httpauthentication
Author:Ho Chun Wei, fuzzybr80(at)gmail.com
Project:PyFileServer, http://pyfilesync.berlios.de/
Copyright:Lesser GNU Public License, see LICENSE file attached with package

WSGI middleware for HTTP basic and digest authentication.

Usage::

from httpauthentication import HTTPAuthenticator

WSGIApp = HTTPAuthenticator(ProtectedWSGIApp, domain_controller, acceptbasic,
acceptdigest, defaultdigest)
where:

ProtectedWSGIApp is the application requiring authenticated access

domain_controller is a domain controller object meeting specific requirements (below)

acceptbasic is a boolean indicating whether to accept requests using the basic authentication scheme (default = True)

acceptdigest is a boolean indicating whether to accept requests using the digest authentication scheme (default = True)

defaultdigest is a boolean. if True, an unauthenticated request will be sent a digest authentication required response, else the unathenticated request will be sent a basic authentication required response (default = True)

The HTTPAuthenticator will put the following authenticated information in the environ dictionary:

environ['httpauthentication.realm'] = realm name
environ['httpauthentication.username'] = username

Domain Controllers

The HTTP basic and digest authentication schemes are based on the following concept:

Each requested relative URI can be resolved to a realm for authentication, for example: /fac_eng/courses/ee5903/timetable.pdf -> might resolve to realm 'Engineering General' /fac_eng/examsolns/ee5903/thisyearssolns.pdf -> might resolve to realm 'Engineering Lecturers' /med_sci/courses/m500/surgery.htm -> might resolve to realm 'Medical Sciences General' and each realm would have a set of username and password pairs that would allow access to the resource.

A domain controller provides this information to the HTTPAuthenticator. This allows developers to write their own domain controllers, that might, for example, interface with their own user database.

for simple applications, a SimpleDomainController is provided that will take in a single realm name (for display) and a single dictionary of username (key) and password (value) string pairs

Usage::
from httpauthentication import SimpleDomainController users = dict(({'John Smith': 'YouNeverGuessMe', 'Dan Brown': 'DontGuessMeEither'}) realm = 'Sample Realm' domain_controller = SimpleDomainController(users, realm)

Domain Controllers must provide the methods as described in pyfileserver.interfaces.domaincontrollerinterface (interface)

The environ variable here is the WSGI 'environ' dictionary. It is passed to all methods of the domain controller as a means for developers to pass information from previous middleware or server config (if required).

Interface

Classes

  • HTTPAuthenticator : WSGI Middleware for basic and digest authenticator.
  • SimpleDomainController : Simple domain controller for HTTPAuthenticator.

pyfiledomaincontroller

Module:pyfileserver.pyfiledomaincontroller
Author:Ho Chun Wei, fuzzybr80(at)gmail.com
Project:PyFileServer, http://pyfilesync.berlios.de/
Copyright:Lesser GNU Public License, see LICENSE file attached with package

This module is specific to the PyFileServer application.

The PyFileServerDomainController fulfills the requirements of a DomainController as used for authentication with httpauthentication.HTTPAuthenticator for the PyFileServer application

Domain Controllers must provide the methods as described in domaincontrollerinterface

See requestresolver.py for more information about user mappings in PyFileServer


extrequestserver

Module:pyfileserver.extrequestserver
Author:Ho Chun Wei, fuzzybr80(at)gmail.com
Project:PyFileServer, http://pyfilesync.berlios.de/
Copyright:Lesser GNU Public License, see LICENSE file attached with package

This is the main implementation module for the various webDAV methods. Each method is implemented as a do<METHOD> generator function that is a wsgi subapplication:

class RequestServer(object)

   constructor :
      __init__(self, propertymanager, 
                     lockmanager)

   main application:      
      __call__(self, environ, start_response)

   application methods:
      doPUT(self, environ, start_response)
      doOPTIONS(self, environ, start_response)
      doGETHEADDirectory(self, environ, start_response)
      doGETHEADFile(self, environ, start_response)
      doMKCOL(self, environ, start_response)
      doDELETE(self, environ, start_response)
      doPROPPATCH(self, environ, start_response)
      doPROPFIND(self, environ, start_response)
      doCOPY(self, environ, start_response)
      doMOVE(self, environ, start_response)
      doLOCK(self, environ, start_response)
      doUNLOCK(self, environ, start_response)

   misc methods:
      evaluateSingleIfConditionalDoException(self, mappedpath, displaypath, 
                                environ, start_response, checkLock = False)
      evaluateSingleHTTPConditionalsDoException(self, mappedpath, 
                                       displaypath, environ, start_response)

This module is specific to the PyFileServer application.

Supporting Objects

The RequestServer takes two supporting objects:

propertymanager

An object that provides storage for dead properties assigned for webDAV resources.

PropertyManagers must provide the methods as described in pyfileserver.interfaces.propertymanagerinterface

See propertylibrary.PropertyManager for a sample implementation using shelve.

lockmanager

An object that provides storage for locks made on webDAV resources.

LockManagers must provide the methods as described in pyfileserver.interfaces.lockmanagerinterface

See locklibrary.LockManager for a sample implementation using shelve.

The RequestServer also uses a resource abstraction layer placed in environ['pyfileserver.resourceAL'] by requestresolver.py

abstractionlayer

An object that provides a basic interface to resources.

This layer allows developers to write layers allowing the application to share resources other than filesystems.

Abstraction Layers must provide the methods as described in pyfileserver.interfaces.abstractionlayerinterface

See fileabstractionlayer.FilesystemAbstractionLayer and fileabstractionlayer.ReadOnlyFilesystemAbstractionLayer for sample implementations based on filesystems.


websupportfuncs

Module:pyfileserver.websupportfuncs
Author:Ho Chun Wei, fuzzybr80(at)gmail.com
Project:PyFileServer, http://pyfilesync.berlios.de/
Copyright:Lesser GNU Public License, see LICENSE file attached with package

This module consists of miscellaneous support functions for PyFileServer:

resource list functions
   recursiveGetPath(resourceAL, dirtorecurs, displaypath, recursfurther, liststore, preadd=True)
   getDepthActionList(resourceAL, mappedpath, displaypath, depthlevel, preadd=True)
   getCopyDepthActionList(depthactionlist, origpath, origdisplaypath, destpath, destdisplaypath)

URL functions
   getLevelUpURL(displayPath)
   cleanUpURL(displayURL)
   cleanUpURLWithoutQuote(displayURL)
   constructFullURL(displaypath, environ)
   getRelativeURL(fullurl, environ)

interpret content range header
   obtainContentRanges(rangetext, filesize)

evaluate HTTP If-Match, if-None-Match, If-Modified-Since, If-Unmodified-Since headers   
   evaluateHTTPConditionals(lastmodifiedsecs, entitytag, environ, isnewfile=False)

evaluate webDAV if header   
   getIfHeaderDict(iftext)
   testIfHeaderDict(dictIf, url, locktokenlist, entitytag, returnlocklist, environ)
   testForLockTokenInIfHeaderDict(dictIf, locktoken, fullurl, headurl)

author note: More documentation here required

This module is specific to the PyFileServer application.


propertylibrary

Module:pyfileserver.propertylibrary
Author:Ho Chun Wei, fuzzybr80(at)gmail.com
Project:PyFileServer, http://pyfilesync.berlios.de/
Copyright:Lesser GNU Public License, see LICENSE file attached with package

This module consists of a number of miscellaneous functions for the dead properties features of webDAV.

It also includes an implementation of a PropertyManager for storage of dead properties. This implementation use shelve for file storage. See extrequestserver.py for details.

PropertyManagers must provide the methods as described in propertymanagerinterface

Properties and PyFileServer

Properties of a resource refers to the attributes of the resource. A property is referenced by the property name and the property namespace. We usually refer to the property as {property namespace}property name

Properties of resources as defined in webdav falls under three categories:

Live properties

These properties are attributes actively maintained by the server, such as file size, or read permissions. if you are sharing a database record as a resource, for example, the attributes of the record could become the live properties of the resource.

The webdav specification defines the following properties that could be live properties (refer to webdav specification for details): {DAV:}creationdate {DAV:}displayname {DAV:}getcontentlanguage {DAV:}getcontentlength {DAV:}getcontenttype {DAV:}getetag {DAV:}getlastmodified {DAV:}resourcetype {DAV:}source

These properties are implemented by the abstraction layer.

Locking properties

They refer to the two webdav-defined properties {DAV:}supportedlock and {DAV:}lockdiscovery

These properties are implemented by the locking library in pyfileserver.locklibrary and dead properties library in pyfileserver.propertylibrary

Dead properties

They refer to arbitrarily assigned properties not actively maintained.

These properties are implemented by the dead properties library in pyfileserver.propertylibrary

Interface

Classes:

class PropertyManager(object)

Misc and Interface methods:

removeProperties(pm, displaypath)
copyProperties(pm, displaypath, destdisplaypath)
writeProperty(pm, resourceAL, mappedpath, displaypath, propns, propname, propupdatemethod, propvalue, reallydoit = True)
getProperty(pm, lm, resourceAL, mappedpath, displaypath, propns, propname)
getApplicablePropertyNames(pm, lm, resourceAL, mappedpath, displaypath)

author note: More documentation here required

This module is specific to the PyFileServer application.


fileabstractionlayer

Module:pyfileserver.fileabstractionlayer
Author:Ho Chun Wei, fuzzybr80(at)gmail.com
Project:PyFileServer, http://pyfilesync.berlios.de/
Copyright:Lesser GNU Public License, see LICENSE file attached with package

This module is specific to the PyFileServer application. It provides two classes FilesystemAbstractionLayer and ReadOnlyFilesystemAbstractionLayer.

Abstraction Layers must provide the methods as described in abstractionlayerinterface

See extrequestserver.py for more information about resource abstraction layers in PyFileServer


loadconfig_primitive

Module:pyfileserver.loadconfig_primitive
Author:Ho Chun Wei, fuzzybr80(at)gmail.com
Project:PyFileServer, http://pyfilesync.berlios.de/
Copyright:Lesser GNU Public License, see LICENSE file attached with package

Loads a python module file returning its module namespace as a dictionary, except all variables starting with '__' (excluding system and built-in objects). A compiled module with the filename suffixed with a 'c' may be created as a byproduct.

If Paste <http://pythonpaste.org> is installed, then paste.pyconfig should be used as a safer and better variant.

functions:

load(filename)

httpdatehelper

Module:pyfileserver.httpdatehelper
Author:Ho Chun Wei, fuzzybr80(at)gmail.com
Project:PyFileServer, http://pyfilesync.berlios.de/
Copyright:Lesser GNU Public License, see LICENSE file attached with package

HTTP dates helper - an assorted library of helpful date functions:

The following time type strings are supported by getsecstime() and getgmtime():

Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format  

TODO

This section documents in varying details some of the issues waiting for resolution or to be done.

Better support for Weak Entity Tags

Move entity tag matching from a simple string match to perhaps matching from the abstraction layer itself. This allows the abstraction layer to implement weak/semantic entity tags

Change of architecture

The application was developed initially as a file-sharing application and then converted to a more generic resource-sharing application. Hence most of the functionality is based on what files would implement.

Need to shift this so that control on what functionality is active is based on the resource layer. Example, if entity tags are not implemented, then entity tag functionality (headers, matching) should be disabled. If {DAV:}getcontentlanguage is implemented in the resource layer, then the server should start returning language headers.... etc.

Gzip Content Encoding

Supporting Gzip Content Encoding normally can be done by middleware - see GzipMiddleWare <http://www.saddi.com/software/py-lib/>

One buggy issue is supporting Content Encoding AND Content Ranges, which does not work too well since Content Ranges is applied after Content Encoding, i.e. you send ranges of the gzipped file and need to know the total length of the gzip file in advance to decode and send the Range headers. Trying to do it in a way that does not buffer unnecessarily (memory or disk).

Encryption Support

This is really in the domain of the webserver itself to support SSL.

Concurrent Conflicting Requests Robustness

This section describes the problem of concurrent conflicting requests, as a result of conflicts at the filesystem level:

Read and Write

Constant GET requests for a file causes it to be file-open locked for reading all the time. No write operations like PUT, DELETE, MOVE can be done even if a write LOCK is obtained since the file remains locked by filesystem level.

Note: some file systems do support deleting a read-open file. The file is deleted but remains available to the read handle until it is closed.

Concurrent Write
Two concurrent write requests, like MOVE and DELETE, for the same resource arrives at the server and is processed in separate threads concurrently. Exact behaviour will depend on the actual resulting interleaved execution but it is likely that some MOVE files will fail as they have been DELETEd and vice versa.

Although the operations themself may fail (interpreting partial success on either side to be failure) a proper response is returned. This does not cause the webserver or filesystem to crash or enter an inconsistent/unstable state.

Most filesharing operations should not have any need to resolve these issues.

Three solutions have been identified:

Optimistic Locking

This assumes that most operations go forward successfully. If it does not due to the filesystem (as evidenced by a 500 Internal Server Error flagged for an operation), then the entire request should be rollbacked and a HTTP 409 Conflict error thrown:

successful = []
try:
    for item in actions:
        successful.append(do_action(item))
except:
    for result in successful:
        result.rollback()
    raise
else:
    for result in successful:
        result.commit()

The server has to ensure that rollback() and commit() operations do not fail. Also partial MOVE/COPY operations may be difficult to rollback.

File Locking

A WRITE request could block until it has obtained all the filesystem level locks on all the files it should process. It needs to address the following:

  • Should the request block indefinitely waiting for the locks or should it adopt optimistic locking (409 Conflict if it fails to get all the locks it wants)
  • Race conditions or deadlocks between concurrent requests waiting for the same locks. Locks should be obtained in a specific order to minimize the possibility of this happening.
Variable Locking

This is a complex locking mechanism, when requests identified as possibly conflicting are prevented from being executed concurrently by a blocking lock. Conflicting requests can be loosely identified as the request URLs being the same or ancestor/descendant of each other (Depth: infinity).

Such a scheme should address the following:

  • Conflicting read operations can be done concurrently but not conflicting write operations
  • Assuming that all requests that are started will complete in finite time, any request should eventually complete.

Last Generated : Wed, 31 Aug 2005 07:10:19 GMT